xref: /petsc/src/ksp/pc/impls/fieldsplit/fieldsplit.c (revision cbb748920e211f6b92d982894ce7f87ce159189c)
1 #include <petsc/private/pcimpl.h>  /*I "petscpc.h" I*/
2 #include <petsc/private/kspimpl.h> /*  This is needed to provide the appropriate PETSC_EXTERN for KSP_Solve_FS ....*/
3 #include <petscdm.h>
4 
5 const char *const PCFieldSplitSchurPreTypes[]  = {"SELF", "SELFP", "A11", "USER", "FULL", "PCFieldSplitSchurPreType", "PC_FIELDSPLIT_SCHUR_PRE_", NULL};
6 const char *const PCFieldSplitSchurFactTypes[] = {"DIAG", "LOWER", "UPPER", "FULL", "PCFieldSplitSchurFactType", "PC_FIELDSPLIT_SCHUR_FACT_", NULL};
7 
8 PetscLogEvent KSP_Solve_FS_0, KSP_Solve_FS_1, KSP_Solve_FS_S, KSP_Solve_FS_U, KSP_Solve_FS_L, KSP_Solve_FS_2, KSP_Solve_FS_3, KSP_Solve_FS_4;
9 
10 typedef struct _PC_FieldSplitLink *PC_FieldSplitLink;
11 struct _PC_FieldSplitLink {
12   KSP               ksp;
13   Vec               x, y, z;
14   char             *splitname;
15   PetscInt          nfields;
16   PetscInt         *fields, *fields_col;
17   VecScatter        sctx;
18   IS                is, is_col;
19   PC_FieldSplitLink next, previous;
20   PetscLogEvent     event;
21 
22   /* Used only when setting coordinates with PCSetCoordinates */
23   PetscInt   dim;
24   PetscInt   ndofs;
25   PetscReal *coords;
26 };
27 
28 typedef struct {
29   PCCompositeType type;
30   PetscBool       defaultsplit; /* Flag for a system with a set of 'k' scalar fields with the same layout (and bs = k) */
31   PetscBool       splitdefined; /* Flag is set after the splits have been defined, to prevent more splits from being added */
32   PetscInt        bs;           /* Block size for IS and Mat structures */
33   PetscInt        nsplits;      /* Number of field divisions defined */
34   Vec            *x, *y, w1, w2;
35   Mat            *mat;    /* The diagonal block for each split */
36   Mat            *pmat;   /* The preconditioning diagonal block for each split */
37   Mat            *Afield; /* The rows of the matrix associated with each split */
38   PetscBool       issetup;
39 
40   /* Only used when Schur complement preconditioning is used */
41   Mat                       B;          /* The (0,1) block */
42   Mat                       C;          /* The (1,0) block */
43   Mat                       schur;      /* The Schur complement S = A11 - A10 A00^{-1} A01, the KSP here, kspinner, is H_1 in [El08] */
44   Mat                       schurp;     /* Assembled approximation to S built by MatSchurComplement to be used as a preconditioning matrix when solving with S */
45   Mat                       schur_user; /* User-provided preconditioning matrix for the Schur complement */
46   PCFieldSplitSchurPreType  schurpre;   /* Determines which preconditioning matrix is used for the Schur complement */
47   PCFieldSplitSchurFactType schurfactorization;
48   KSP                       kspschur;   /* The solver for S */
49   KSP                       kspupper;   /* The solver for A in the upper diagonal part of the factorization (H_2 in [El08]) */
50   PetscScalar               schurscale; /* Scaling factor for the Schur complement solution with DIAG factorization */
51 
52   /* Only used when Golub-Kahan bidiagonalization preconditioning is used */
53   Mat          H;           /* The modified matrix H = A00 + nu*A01*A01'              */
54   PetscReal    gkbtol;      /* Stopping tolerance for lower bound estimate            */
55   PetscInt     gkbdelay;    /* The delay window for the stopping criterion            */
56   PetscReal    gkbnu;       /* Parameter for augmented Lagrangian H = A + nu*A01*A01' */
57   PetscInt     gkbmaxit;    /* Maximum number of iterations for outer loop            */
58   PetscBool    gkbmonitor;  /* Monitor for gkb iterations and the lower bound error   */
59   PetscViewer  gkbviewer;   /* Viewer context for gkbmonitor                          */
60   Vec          u, v, d, Hu; /* Work vectors for the GKB algorithm                     */
61   PetscScalar *vecz;        /* Contains intermediate values, eg for lower bound       */
62 
63   PC_FieldSplitLink head;
64   PetscBool         isrestrict;       /* indicates PCFieldSplitRestrictIS() has been last called on this object, hack */
65   PetscBool         suboptionsset;    /* Indicates that the KSPSetFromOptions() has been called on the sub-KSPs */
66   PetscBool         dm_splits;        /* Whether to use DMCreateFieldDecomposition() whenever possible */
67   PetscBool         diag_use_amat;    /* Whether to extract diagonal matrix blocks from Amat, rather than Pmat (weaker than -pc_use_amat) */
68   PetscBool         offdiag_use_amat; /* Whether to extract off-diagonal matrix blocks from Amat, rather than Pmat (weaker than -pc_use_amat) */
69   PetscBool         detect;           /* Whether to form 2-way split by finding zero diagonal entries */
70   PetscBool         coordinates_set;  /* Whether PCSetCoordinates has been called */
71 } PC_FieldSplit;
72 
73 /*
74     Note:
75     there is no particular reason that pmat, x, and y are stored as arrays in PC_FieldSplit instead of
76    inside PC_FieldSplitLink, just historical. If you want to be able to add new fields after already using the
77    PC you could change this.
78 */
79 
80 /* This helper is so that setting a user-provided preconditioning matrix is orthogonal to choosing to use it.  This way the
81 * application-provided FormJacobian can provide this matrix without interfering with the user's (command-line) choices. */
82 static Mat FieldSplitSchurPre(PC_FieldSplit *jac)
83 {
84   switch (jac->schurpre) {
85   case PC_FIELDSPLIT_SCHUR_PRE_SELF:
86     return jac->schur;
87   case PC_FIELDSPLIT_SCHUR_PRE_SELFP:
88     return jac->schurp;
89   case PC_FIELDSPLIT_SCHUR_PRE_A11:
90     return jac->pmat[1];
91   case PC_FIELDSPLIT_SCHUR_PRE_FULL: /* We calculate this and store it in schur_user */
92   case PC_FIELDSPLIT_SCHUR_PRE_USER: /* Use a user-provided matrix if it is given, otherwise diagonal block */
93   default:
94     return jac->schur_user ? jac->schur_user : jac->pmat[1];
95   }
96 }
97 
98 #include <petscdraw.h>
99 static PetscErrorCode PCView_FieldSplit(PC pc, PetscViewer viewer)
100 {
101   PC_FieldSplit    *jac = (PC_FieldSplit *)pc->data;
102   PetscBool         iascii, isdraw;
103   PetscInt          i, j;
104   PC_FieldSplitLink ilink = jac->head;
105 
106   PetscFunctionBegin;
107   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
108   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
109   if (iascii) {
110     if (jac->bs > 0) {
111       PetscCall(PetscViewerASCIIPrintf(viewer, "  FieldSplit with %s composition: total splits = %" PetscInt_FMT ", blocksize = %" PetscInt_FMT "\n", PCCompositeTypes[jac->type], jac->nsplits, jac->bs));
112     } else {
113       PetscCall(PetscViewerASCIIPrintf(viewer, "  FieldSplit with %s composition: total splits = %" PetscInt_FMT "\n", PCCompositeTypes[jac->type], jac->nsplits));
114     }
115     if (pc->useAmat) PetscCall(PetscViewerASCIIPrintf(viewer, "  using Amat (not Pmat) as operator for blocks\n"));
116     if (jac->diag_use_amat) PetscCall(PetscViewerASCIIPrintf(viewer, "  using Amat (not Pmat) as operator for diagonal blocks\n"));
117     if (jac->offdiag_use_amat) PetscCall(PetscViewerASCIIPrintf(viewer, "  using Amat (not Pmat) as operator for off-diagonal blocks\n"));
118     PetscCall(PetscViewerASCIIPrintf(viewer, "  Solver info for each split is in the following KSP objects:\n"));
119     for (i = 0; i < jac->nsplits; i++) {
120       if (ilink->fields) {
121         PetscCall(PetscViewerASCIIPrintf(viewer, "Split number %" PetscInt_FMT " Fields ", i));
122         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
123         for (j = 0; j < ilink->nfields; j++) {
124           if (j > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ","));
125           PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, ilink->fields[j]));
126         }
127         PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
128         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
129       } else {
130         PetscCall(PetscViewerASCIIPrintf(viewer, "Split number %" PetscInt_FMT " Defined by IS\n", i));
131       }
132       PetscCall(KSPView(ilink->ksp, viewer));
133       ilink = ilink->next;
134     }
135   }
136 
137   if (isdraw) {
138     PetscDraw draw;
139     PetscReal x, y, w, wd;
140 
141     PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
142     PetscCall(PetscDrawGetCurrentPoint(draw, &x, &y));
143     w  = 2 * PetscMin(1.0 - x, x);
144     wd = w / (jac->nsplits + 1);
145     x  = x - wd * (jac->nsplits - 1) / 2.0;
146     for (i = 0; i < jac->nsplits; i++) {
147       PetscCall(PetscDrawPushCurrentPoint(draw, x, y));
148       PetscCall(KSPView(ilink->ksp, viewer));
149       PetscCall(PetscDrawPopCurrentPoint(draw));
150       x += wd;
151       ilink = ilink->next;
152     }
153   }
154   PetscFunctionReturn(PETSC_SUCCESS);
155 }
156 
157 static PetscErrorCode PCView_FieldSplit_Schur(PC pc, PetscViewer viewer)
158 {
159   PC_FieldSplit             *jac = (PC_FieldSplit *)pc->data;
160   PetscBool                  iascii, isdraw;
161   PetscInt                   i, j;
162   PC_FieldSplitLink          ilink = jac->head;
163   MatSchurComplementAinvType atype;
164 
165   PetscFunctionBegin;
166   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
167   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
168   if (iascii) {
169     if (jac->bs > 0) {
170       PetscCall(PetscViewerASCIIPrintf(viewer, "  FieldSplit with Schur preconditioner, blocksize = %" PetscInt_FMT ", factorization %s\n", jac->bs, PCFieldSplitSchurFactTypes[jac->schurfactorization]));
171     } else {
172       PetscCall(PetscViewerASCIIPrintf(viewer, "  FieldSplit with Schur preconditioner, factorization %s\n", PCFieldSplitSchurFactTypes[jac->schurfactorization]));
173     }
174     if (pc->useAmat) PetscCall(PetscViewerASCIIPrintf(viewer, "  using Amat (not Pmat) as operator for blocks\n"));
175     switch (jac->schurpre) {
176     case PC_FIELDSPLIT_SCHUR_PRE_SELF:
177       PetscCall(PetscViewerASCIIPrintf(viewer, "  Preconditioner for the Schur complement formed from S itself\n"));
178       break;
179     case PC_FIELDSPLIT_SCHUR_PRE_SELFP:
180       if (jac->schur) {
181         PetscCall(MatSchurComplementGetAinvType(jac->schur, &atype));
182         PetscCall(PetscViewerASCIIPrintf(viewer, "  Preconditioner for the Schur complement formed from Sp, an assembled approximation to S, which uses A00's %sinverse\n", atype == MAT_SCHUR_COMPLEMENT_AINV_DIAG ? "diagonal's " : (atype == MAT_SCHUR_COMPLEMENT_AINV_BLOCK_DIAG ? "block diagonal's " : (atype == MAT_SCHUR_COMPLEMENT_AINV_FULL ? "full " : "lumped diagonal's "))));
183       }
184       break;
185     case PC_FIELDSPLIT_SCHUR_PRE_A11:
186       PetscCall(PetscViewerASCIIPrintf(viewer, "  Preconditioner for the Schur complement formed from A11\n"));
187       break;
188     case PC_FIELDSPLIT_SCHUR_PRE_FULL:
189       PetscCall(PetscViewerASCIIPrintf(viewer, "  Preconditioner for the Schur complement formed from the exact Schur complement\n"));
190       break;
191     case PC_FIELDSPLIT_SCHUR_PRE_USER:
192       if (jac->schur_user) {
193         PetscCall(PetscViewerASCIIPrintf(viewer, "  Preconditioner for the Schur complement formed from user provided matrix\n"));
194       } else {
195         PetscCall(PetscViewerASCIIPrintf(viewer, "  Preconditioner for the Schur complement formed from A11\n"));
196       }
197       break;
198     default:
199       SETERRQ(PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_OUTOFRANGE, "Invalid Schur preconditioning type: %d", jac->schurpre);
200     }
201     PetscCall(PetscViewerASCIIPrintf(viewer, "  Split info:\n"));
202     PetscCall(PetscViewerASCIIPushTab(viewer));
203     for (i = 0; i < jac->nsplits; i++) {
204       if (ilink->fields) {
205         PetscCall(PetscViewerASCIIPrintf(viewer, "Split number %" PetscInt_FMT " Fields ", i));
206         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
207         for (j = 0; j < ilink->nfields; j++) {
208           if (j > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ","));
209           PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, ilink->fields[j]));
210         }
211         PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
212         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
213       } else {
214         PetscCall(PetscViewerASCIIPrintf(viewer, "Split number %" PetscInt_FMT " Defined by IS\n", i));
215       }
216       ilink = ilink->next;
217     }
218     PetscCall(PetscViewerASCIIPrintf(viewer, "KSP solver for A00 block\n"));
219     PetscCall(PetscViewerASCIIPushTab(viewer));
220     if (jac->head) {
221       PetscCall(KSPView(jac->head->ksp, viewer));
222     } else PetscCall(PetscViewerASCIIPrintf(viewer, "  not yet available\n"));
223     PetscCall(PetscViewerASCIIPopTab(viewer));
224     if (jac->head && jac->kspupper != jac->head->ksp) {
225       PetscCall(PetscViewerASCIIPrintf(viewer, "KSP solver for upper A00 in upper triangular factor\n"));
226       PetscCall(PetscViewerASCIIPushTab(viewer));
227       if (jac->kspupper) PetscCall(KSPView(jac->kspupper, viewer));
228       else PetscCall(PetscViewerASCIIPrintf(viewer, "  not yet available\n"));
229       PetscCall(PetscViewerASCIIPopTab(viewer));
230     }
231     PetscCall(PetscViewerASCIIPrintf(viewer, "KSP solver for S = A11 - A10 inv(A00) A01\n"));
232     PetscCall(PetscViewerASCIIPushTab(viewer));
233     if (jac->kspschur) {
234       PetscCall(KSPView(jac->kspschur, viewer));
235     } else {
236       PetscCall(PetscViewerASCIIPrintf(viewer, "  not yet available\n"));
237     }
238     PetscCall(PetscViewerASCIIPopTab(viewer));
239     PetscCall(PetscViewerASCIIPopTab(viewer));
240   } else if (isdraw && jac->head) {
241     PetscDraw draw;
242     PetscReal x, y, w, wd, h;
243     PetscInt  cnt = 2;
244     char      str[32];
245 
246     PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
247     PetscCall(PetscDrawGetCurrentPoint(draw, &x, &y));
248     if (jac->kspupper != jac->head->ksp) cnt++;
249     w  = 2 * PetscMin(1.0 - x, x);
250     wd = w / (cnt + 1);
251 
252     PetscCall(PetscSNPrintf(str, 32, "Schur fact. %s", PCFieldSplitSchurFactTypes[jac->schurfactorization]));
253     PetscCall(PetscDrawStringBoxed(draw, x, y, PETSC_DRAW_RED, PETSC_DRAW_BLACK, str, NULL, &h));
254     y -= h;
255     if (jac->schurpre == PC_FIELDSPLIT_SCHUR_PRE_USER && !jac->schur_user) {
256       PetscCall(PetscSNPrintf(str, 32, "Prec. for Schur from %s", PCFieldSplitSchurPreTypes[PC_FIELDSPLIT_SCHUR_PRE_A11]));
257     } else {
258       PetscCall(PetscSNPrintf(str, 32, "Prec. for Schur from %s", PCFieldSplitSchurPreTypes[jac->schurpre]));
259     }
260     PetscCall(PetscDrawStringBoxed(draw, x + wd * (cnt - 1) / 2.0, y, PETSC_DRAW_RED, PETSC_DRAW_BLACK, str, NULL, &h));
261     y -= h;
262     x = x - wd * (cnt - 1) / 2.0;
263 
264     PetscCall(PetscDrawPushCurrentPoint(draw, x, y));
265     PetscCall(KSPView(jac->head->ksp, viewer));
266     PetscCall(PetscDrawPopCurrentPoint(draw));
267     if (jac->kspupper != jac->head->ksp) {
268       x += wd;
269       PetscCall(PetscDrawPushCurrentPoint(draw, x, y));
270       PetscCall(KSPView(jac->kspupper, viewer));
271       PetscCall(PetscDrawPopCurrentPoint(draw));
272     }
273     x += wd;
274     PetscCall(PetscDrawPushCurrentPoint(draw, x, y));
275     PetscCall(KSPView(jac->kspschur, viewer));
276     PetscCall(PetscDrawPopCurrentPoint(draw));
277   }
278   PetscFunctionReturn(PETSC_SUCCESS);
279 }
280 
281 static PetscErrorCode PCView_FieldSplit_GKB(PC pc, PetscViewer viewer)
282 {
283   PC_FieldSplit    *jac = (PC_FieldSplit *)pc->data;
284   PetscBool         iascii, isdraw;
285   PetscInt          i, j;
286   PC_FieldSplitLink ilink = jac->head;
287 
288   PetscFunctionBegin;
289   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
290   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
291   if (iascii) {
292     if (jac->bs > 0) {
293       PetscCall(PetscViewerASCIIPrintf(viewer, "  FieldSplit with %s composition: total splits = %" PetscInt_FMT ", blocksize = %" PetscInt_FMT "\n", PCCompositeTypes[jac->type], jac->nsplits, jac->bs));
294     } else {
295       PetscCall(PetscViewerASCIIPrintf(viewer, "  FieldSplit with %s composition: total splits = %" PetscInt_FMT "\n", PCCompositeTypes[jac->type], jac->nsplits));
296     }
297     if (pc->useAmat) PetscCall(PetscViewerASCIIPrintf(viewer, "  using Amat (not Pmat) as operator for blocks\n"));
298     if (jac->diag_use_amat) PetscCall(PetscViewerASCIIPrintf(viewer, "  using Amat (not Pmat) as operator for diagonal blocks\n"));
299     if (jac->offdiag_use_amat) PetscCall(PetscViewerASCIIPrintf(viewer, "  using Amat (not Pmat) as operator for off-diagonal blocks\n"));
300 
301     PetscCall(PetscViewerASCIIPrintf(viewer, "  Stopping tolerance=%.1e, delay in error estimate=%" PetscInt_FMT ", maximum iterations=%" PetscInt_FMT "\n", (double)jac->gkbtol, jac->gkbdelay, jac->gkbmaxit));
302     PetscCall(PetscViewerASCIIPrintf(viewer, "  Solver info for H = A00 + nu*A01*A01' matrix:\n"));
303     PetscCall(PetscViewerASCIIPushTab(viewer));
304 
305     if (ilink->fields) {
306       PetscCall(PetscViewerASCIIPrintf(viewer, "Split number 0 Fields "));
307       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
308       for (j = 0; j < ilink->nfields; j++) {
309         if (j > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ","));
310         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, ilink->fields[j]));
311       }
312       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
313       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
314     } else {
315       PetscCall(PetscViewerASCIIPrintf(viewer, "Split number 0 Defined by IS\n"));
316     }
317     PetscCall(KSPView(ilink->ksp, viewer));
318 
319     PetscCall(PetscViewerASCIIPopTab(viewer));
320   }
321 
322   if (isdraw) {
323     PetscDraw draw;
324     PetscReal x, y, w, wd;
325 
326     PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
327     PetscCall(PetscDrawGetCurrentPoint(draw, &x, &y));
328     w  = 2 * PetscMin(1.0 - x, x);
329     wd = w / (jac->nsplits + 1);
330     x  = x - wd * (jac->nsplits - 1) / 2.0;
331     for (i = 0; i < jac->nsplits; i++) {
332       PetscCall(PetscDrawPushCurrentPoint(draw, x, y));
333       PetscCall(KSPView(ilink->ksp, viewer));
334       PetscCall(PetscDrawPopCurrentPoint(draw));
335       x += wd;
336       ilink = ilink->next;
337     }
338   }
339   PetscFunctionReturn(PETSC_SUCCESS);
340 }
341 
342 /* Precondition: jac->bs is set to a meaningful value */
343 static PetscErrorCode PCFieldSplitSetRuntimeSplits_Private(PC pc)
344 {
345   PC_FieldSplit *jac = (PC_FieldSplit *)pc->data;
346   PetscInt       i, nfields, *ifields, nfields_col, *ifields_col;
347   PetscBool      flg, flg_col;
348   char           optionname[128], splitname[8], optionname_col[128];
349 
350   PetscFunctionBegin;
351   PetscCall(PetscMalloc1(jac->bs, &ifields));
352   PetscCall(PetscMalloc1(jac->bs, &ifields_col));
353   for (i = 0, flg = PETSC_TRUE;; i++) {
354     PetscCall(PetscSNPrintf(splitname, sizeof(splitname), "%" PetscInt_FMT, i));
355     PetscCall(PetscSNPrintf(optionname, sizeof(optionname), "-pc_fieldsplit_%" PetscInt_FMT "_fields", i));
356     PetscCall(PetscSNPrintf(optionname_col, sizeof(optionname_col), "-pc_fieldsplit_%" PetscInt_FMT "_fields_col", i));
357     nfields     = jac->bs;
358     nfields_col = jac->bs;
359     PetscCall(PetscOptionsGetIntArray(((PetscObject)pc)->options, ((PetscObject)pc)->prefix, optionname, ifields, &nfields, &flg));
360     PetscCall(PetscOptionsGetIntArray(((PetscObject)pc)->options, ((PetscObject)pc)->prefix, optionname_col, ifields_col, &nfields_col, &flg_col));
361     if (!flg) break;
362     else if (flg && !flg_col) {
363       PetscCheck(nfields, PETSC_COMM_SELF, PETSC_ERR_USER, "Cannot list zero fields");
364       PetscCall(PCFieldSplitSetFields(pc, splitname, nfields, ifields, ifields));
365     } else {
366       PetscCheck(nfields && nfields_col, PETSC_COMM_SELF, PETSC_ERR_USER, "Cannot list zero fields");
367       PetscCheck(nfields == nfields_col, PETSC_COMM_SELF, PETSC_ERR_USER, "Number of row and column fields must match");
368       PetscCall(PCFieldSplitSetFields(pc, splitname, nfields, ifields, ifields_col));
369     }
370   }
371   if (i > 0) {
372     /* Makes command-line setting of splits take precedence over setting them in code.
373        Otherwise subsequent calls to PCFieldSplitSetIS() or PCFieldSplitSetFields() would
374        create new splits, which would probably not be what the user wanted. */
375     jac->splitdefined = PETSC_TRUE;
376   }
377   PetscCall(PetscFree(ifields));
378   PetscCall(PetscFree(ifields_col));
379   PetscFunctionReturn(PETSC_SUCCESS);
380 }
381 
382 static PetscErrorCode PCFieldSplitSetDefaults(PC pc)
383 {
384   PC_FieldSplit    *jac                = (PC_FieldSplit *)pc->data;
385   PC_FieldSplitLink ilink              = jac->head;
386   PetscBool         fieldsplit_default = PETSC_FALSE, coupling = PETSC_FALSE;
387   PetscInt          i;
388 
389   PetscFunctionBegin;
390   /*
391    Kinda messy, but at least this now uses DMCreateFieldDecomposition().
392    Should probably be rewritten.
393    */
394   if (!ilink) {
395     PetscCall(PetscOptionsGetBool(((PetscObject)pc)->options, ((PetscObject)pc)->prefix, "-pc_fieldsplit_detect_coupling", &coupling, NULL));
396     if (pc->dm && jac->dm_splits && !jac->detect && !coupling) {
397       PetscInt  numFields, f, i, j;
398       char    **fieldNames;
399       IS       *fields;
400       DM       *dms;
401       DM        subdm[128];
402       PetscBool flg;
403 
404       PetscCall(DMCreateFieldDecomposition(pc->dm, &numFields, &fieldNames, &fields, &dms));
405       /* Allow the user to prescribe the splits */
406       for (i = 0, flg = PETSC_TRUE;; i++) {
407         PetscInt ifields[128];
408         IS       compField;
409         char     optionname[128], splitname[8];
410         PetscInt nfields = numFields;
411 
412         PetscCall(PetscSNPrintf(optionname, sizeof(optionname), "-pc_fieldsplit_%" PetscInt_FMT "_fields", i));
413         PetscCall(PetscOptionsGetIntArray(((PetscObject)pc)->options, ((PetscObject)pc)->prefix, optionname, ifields, &nfields, &flg));
414         if (!flg) break;
415         PetscCheck(numFields <= 128, PetscObjectComm((PetscObject)pc), PETSC_ERR_SUP, "Cannot currently support %" PetscInt_FMT " > 128 fields", numFields);
416         PetscCall(DMCreateSubDM(pc->dm, nfields, ifields, &compField, &subdm[i]));
417         if (nfields == 1) {
418           PetscCall(PCFieldSplitSetIS(pc, fieldNames[ifields[0]], compField));
419         } else {
420           PetscCall(PetscSNPrintf(splitname, sizeof(splitname), "%" PetscInt_FMT, i));
421           PetscCall(PCFieldSplitSetIS(pc, splitname, compField));
422         }
423         PetscCall(ISDestroy(&compField));
424         for (j = 0; j < nfields; ++j) {
425           f = ifields[j];
426           PetscCall(PetscFree(fieldNames[f]));
427           PetscCall(ISDestroy(&fields[f]));
428         }
429       }
430       if (i == 0) {
431         for (f = 0; f < numFields; ++f) {
432           PetscCall(PCFieldSplitSetIS(pc, fieldNames[f], fields[f]));
433           PetscCall(PetscFree(fieldNames[f]));
434           PetscCall(ISDestroy(&fields[f]));
435         }
436       } else {
437         for (j = 0; j < numFields; j++) PetscCall(DMDestroy(dms + j));
438         PetscCall(PetscFree(dms));
439         PetscCall(PetscMalloc1(i, &dms));
440         for (j = 0; j < i; ++j) dms[j] = subdm[j];
441       }
442       PetscCall(PetscFree(fieldNames));
443       PetscCall(PetscFree(fields));
444       if (dms) {
445         PetscCall(PetscInfo(pc, "Setting up physics based fieldsplit preconditioner using the embedded DM\n"));
446         for (ilink = jac->head, i = 0; ilink; ilink = ilink->next, ++i) {
447           const char *prefix;
448           PetscCall(PetscObjectGetOptionsPrefix((PetscObject)(ilink->ksp), &prefix));
449           PetscCall(PetscObjectSetOptionsPrefix((PetscObject)(dms[i]), prefix));
450           PetscCall(KSPSetDM(ilink->ksp, dms[i]));
451           PetscCall(KSPSetDMActive(ilink->ksp, PETSC_FALSE));
452           {
453             PetscErrorCode (*func)(KSP, Mat, Mat, void *);
454             void *ctx;
455 
456             PetscCall(DMKSPGetComputeOperators(pc->dm, &func, &ctx));
457             PetscCall(DMKSPSetComputeOperators(dms[i], func, ctx));
458           }
459           PetscCall(PetscObjectIncrementTabLevel((PetscObject)dms[i], (PetscObject)ilink->ksp, 0));
460           PetscCall(DMDestroy(&dms[i]));
461         }
462         PetscCall(PetscFree(dms));
463       }
464     } else {
465       if (jac->bs <= 0) {
466         if (pc->pmat) {
467           PetscCall(MatGetBlockSize(pc->pmat, &jac->bs));
468         } else jac->bs = 1;
469       }
470 
471       if (jac->detect) {
472         IS       zerodiags, rest;
473         PetscInt nmin, nmax;
474 
475         PetscCall(MatGetOwnershipRange(pc->mat, &nmin, &nmax));
476         if (jac->diag_use_amat) {
477           PetscCall(MatFindZeroDiagonals(pc->mat, &zerodiags));
478         } else {
479           PetscCall(MatFindZeroDiagonals(pc->pmat, &zerodiags));
480         }
481         PetscCall(ISComplement(zerodiags, nmin, nmax, &rest));
482         PetscCall(PCFieldSplitSetIS(pc, "0", rest));
483         PetscCall(PCFieldSplitSetIS(pc, "1", zerodiags));
484         PetscCall(ISDestroy(&zerodiags));
485         PetscCall(ISDestroy(&rest));
486       } else if (coupling) {
487         IS       coupling, rest;
488         PetscInt nmin, nmax;
489 
490         PetscCall(MatGetOwnershipRange(pc->mat, &nmin, &nmax));
491         if (jac->offdiag_use_amat) {
492           PetscCall(MatFindOffBlockDiagonalEntries(pc->mat, &coupling));
493         } else {
494           PetscCall(MatFindOffBlockDiagonalEntries(pc->pmat, &coupling));
495         }
496         PetscCall(ISCreateStride(PetscObjectComm((PetscObject)pc->mat), nmax - nmin, nmin, 1, &rest));
497         PetscCall(ISSetIdentity(rest));
498         PetscCall(PCFieldSplitSetIS(pc, "0", rest));
499         PetscCall(PCFieldSplitSetIS(pc, "1", coupling));
500         PetscCall(ISDestroy(&coupling));
501         PetscCall(ISDestroy(&rest));
502       } else {
503         PetscCall(PetscOptionsGetBool(((PetscObject)pc)->options, ((PetscObject)pc)->prefix, "-pc_fieldsplit_default", &fieldsplit_default, NULL));
504         if (!fieldsplit_default) {
505           /* Allow user to set fields from command line,  if bs was known at the time of PCSetFromOptions_FieldSplit()
506            then it is set there. This is not ideal because we should only have options set in XXSetFromOptions(). */
507           PetscCall(PCFieldSplitSetRuntimeSplits_Private(pc));
508           if (jac->splitdefined) PetscCall(PetscInfo(pc, "Splits defined using the options database\n"));
509         }
510         if ((fieldsplit_default || !jac->splitdefined) && !jac->isrestrict) {
511           Mat       M = pc->pmat;
512           PetscBool isnest;
513 
514           PetscCall(PetscInfo(pc, "Using default splitting of fields\n"));
515           PetscCall(PetscObjectTypeCompare((PetscObject)pc->pmat, MATNEST, &isnest));
516           if (!isnest) {
517             M = pc->mat;
518             PetscCall(PetscObjectTypeCompare((PetscObject)pc->mat, MATNEST, &isnest));
519           }
520           if (isnest) {
521             IS      *fields;
522             PetscInt nf;
523 
524             PetscCall(MatNestGetSize(M, &nf, NULL));
525             PetscCall(PetscMalloc1(nf, &fields));
526             PetscCall(MatNestGetISs(M, fields, NULL));
527             for (i = 0; i < nf; i++) PetscCall(PCFieldSplitSetIS(pc, NULL, fields[i]));
528             PetscCall(PetscFree(fields));
529           } else {
530             for (i = 0; i < jac->bs; i++) {
531               char splitname[8];
532               PetscCall(PetscSNPrintf(splitname, sizeof(splitname), "%" PetscInt_FMT, i));
533               PetscCall(PCFieldSplitSetFields(pc, splitname, 1, &i, &i));
534             }
535             jac->defaultsplit = PETSC_TRUE;
536           }
537         }
538       }
539     }
540   } else if (jac->nsplits == 1) {
541     IS       is2;
542     PetscInt nmin, nmax;
543 
544     PetscCheck(ilink->is, PetscObjectComm((PetscObject)pc), PETSC_ERR_SUP, "Must provide at least two sets of fields to PCFieldSplit()");
545     PetscCall(MatGetOwnershipRange(pc->mat, &nmin, &nmax));
546     PetscCall(ISComplement(ilink->is, nmin, nmax, &is2));
547     PetscCall(PCFieldSplitSetIS(pc, "1", is2));
548     PetscCall(ISDestroy(&is2));
549   }
550 
551   PetscCheck(jac->nsplits >= 2, PetscObjectComm((PetscObject)pc), PETSC_ERR_PLIB, "Unhandled case, must have at least two fields, not %" PetscInt_FMT, jac->nsplits);
552   PetscFunctionReturn(PETSC_SUCCESS);
553 }
554 
555 static PetscErrorCode MatGolubKahanComputeExplicitOperator(Mat A, Mat B, Mat C, Mat *H, PetscReal gkbnu)
556 {
557   Mat       BT, T;
558   PetscReal nrmT, nrmB;
559 
560   PetscFunctionBegin;
561   PetscCall(MatHermitianTranspose(C, MAT_INITIAL_MATRIX, &T)); /* Test if augmented matrix is symmetric */
562   PetscCall(MatAXPY(T, -1.0, B, DIFFERENT_NONZERO_PATTERN));
563   PetscCall(MatNorm(T, NORM_1, &nrmT));
564   PetscCall(MatNorm(B, NORM_1, &nrmB));
565   PetscCheck(nrmB <= 0 || nrmT / nrmB < PETSC_SMALL, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Matrix is not symmetric/hermitian, GKB is not applicable.");
566 
567   /* Compute augmented Lagrangian matrix H = A00 + nu*A01*A01'. This corresponds to */
568   /* setting N := 1/nu*I in [Ar13].                                                 */
569   PetscCall(MatHermitianTranspose(B, MAT_INITIAL_MATRIX, &BT));
570   PetscCall(MatMatMult(B, BT, MAT_INITIAL_MATRIX, PETSC_DEFAULT, H)); /* H = A01*A01'          */
571   PetscCall(MatAYPX(*H, gkbnu, A, DIFFERENT_NONZERO_PATTERN));        /* H = A00 + nu*A01*A01' */
572 
573   PetscCall(MatDestroy(&BT));
574   PetscCall(MatDestroy(&T));
575   PetscFunctionReturn(PETSC_SUCCESS);
576 }
577 
578 PETSC_EXTERN PetscErrorCode PetscOptionsFindPairPrefix_Private(PetscOptions, const char pre[], const char name[], const char *value[], PetscBool *flg);
579 
580 static PetscErrorCode PCSetUp_FieldSplit(PC pc)
581 {
582   PC_FieldSplit    *jac = (PC_FieldSplit *)pc->data;
583   PC_FieldSplitLink ilink;
584   PetscInt          i, nsplit;
585   PetscBool         sorted, sorted_col;
586 
587   PetscFunctionBegin;
588   pc->failedreason = PC_NOERROR;
589   PetscCall(PCFieldSplitSetDefaults(pc));
590   nsplit = jac->nsplits;
591   ilink  = jac->head;
592 
593   /* get the matrices for each split */
594   if (!jac->issetup) {
595     PetscInt rstart, rend, nslots, bs;
596 
597     jac->issetup = PETSC_TRUE;
598 
599     /* This is done here instead of in PCFieldSplitSetFields() because may not have matrix at that point */
600     if (jac->defaultsplit || !ilink->is) {
601       if (jac->bs <= 0) jac->bs = nsplit;
602     }
603 
604     /*  MatCreateSubMatrix() for [S]BAIJ matrices can only work if the indices include entire blocks of the matrix */
605     PetscCall(MatGetBlockSize(pc->pmat, &bs));
606     if (bs > 1 && (jac->bs <= bs || jac->bs % bs)) {
607       PetscBool blk;
608 
609       PetscCall(PetscObjectTypeCompareAny((PetscObject)pc->pmat, &blk, MATBAIJ, MATSBAIJ, MATSEQBAIJ, MATSEQSBAIJ, MATMPIBAIJ, MATMPISBAIJ, NULL));
610       PetscCheck(!blk, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_WRONG, "Cannot use MATBAIJ with PCFIELDSPLIT and currently set matrix and PC blocksizes");
611     }
612 
613     bs = jac->bs;
614     PetscCall(MatGetOwnershipRange(pc->pmat, &rstart, &rend));
615     nslots = (rend - rstart) / bs;
616     for (i = 0; i < nsplit; i++) {
617       if (jac->defaultsplit) {
618         PetscCall(ISCreateStride(PetscObjectComm((PetscObject)pc), nslots, rstart + i, nsplit, &ilink->is));
619         PetscCall(ISDuplicate(ilink->is, &ilink->is_col));
620       } else if (!ilink->is) {
621         if (ilink->nfields > 1) {
622           PetscInt *ii, *jj, j, k, nfields = ilink->nfields, *fields = ilink->fields, *fields_col = ilink->fields_col;
623           PetscCall(PetscMalloc1(ilink->nfields * nslots, &ii));
624           PetscCall(PetscMalloc1(ilink->nfields * nslots, &jj));
625           for (j = 0; j < nslots; j++) {
626             for (k = 0; k < nfields; k++) {
627               ii[nfields * j + k] = rstart + bs * j + fields[k];
628               jj[nfields * j + k] = rstart + bs * j + fields_col[k];
629             }
630           }
631           PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)pc), nslots * nfields, ii, PETSC_OWN_POINTER, &ilink->is));
632           PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)pc), nslots * nfields, jj, PETSC_OWN_POINTER, &ilink->is_col));
633           PetscCall(ISSetBlockSize(ilink->is, nfields));
634           PetscCall(ISSetBlockSize(ilink->is_col, nfields));
635         } else {
636           PetscCall(ISCreateStride(PetscObjectComm((PetscObject)pc), nslots, rstart + ilink->fields[0], bs, &ilink->is));
637           PetscCall(ISCreateStride(PetscObjectComm((PetscObject)pc), nslots, rstart + ilink->fields_col[0], bs, &ilink->is_col));
638         }
639       }
640       PetscCall(ISSorted(ilink->is, &sorted));
641       if (ilink->is_col) PetscCall(ISSorted(ilink->is_col, &sorted_col));
642       PetscCheck(sorted && sorted_col, PETSC_COMM_SELF, PETSC_ERR_USER, "Fields must be sorted when creating split");
643       ilink = ilink->next;
644     }
645   }
646 
647   ilink = jac->head;
648   if (!jac->pmat) {
649     Vec xtmp;
650 
651     PetscCall(MatCreateVecs(pc->pmat, &xtmp, NULL));
652     PetscCall(PetscMalloc1(nsplit, &jac->pmat));
653     PetscCall(PetscMalloc2(nsplit, &jac->x, nsplit, &jac->y));
654     for (i = 0; i < nsplit; i++) {
655       MatNullSpace sp;
656 
657       /* Check for preconditioning matrix attached to IS */
658       PetscCall(PetscObjectQuery((PetscObject)ilink->is, "pmat", (PetscObject *)&jac->pmat[i]));
659       if (jac->pmat[i]) {
660         PetscCall(PetscObjectReference((PetscObject)jac->pmat[i]));
661         if (jac->type == PC_COMPOSITE_SCHUR) {
662           jac->schur_user = jac->pmat[i];
663 
664           PetscCall(PetscObjectReference((PetscObject)jac->schur_user));
665         }
666       } else {
667         const char *prefix;
668         PetscCall(MatCreateSubMatrix(pc->pmat, ilink->is, ilink->is_col, MAT_INITIAL_MATRIX, &jac->pmat[i]));
669         PetscCall(MatGetOptionsPrefix(jac->pmat[i], &prefix));
670         if (!prefix) {
671           PetscCall(KSPGetOptionsPrefix(ilink->ksp, &prefix));
672           PetscCall(MatSetOptionsPrefix(jac->pmat[i], prefix));
673         }
674         PetscCall(MatSetFromOptions(jac->pmat[i]));
675         PetscCall(MatViewFromOptions(jac->pmat[i], NULL, "-mat_view"));
676       }
677       /* create work vectors for each split */
678       PetscCall(MatCreateVecs(jac->pmat[i], &jac->x[i], &jac->y[i]));
679       ilink->x = jac->x[i];
680       ilink->y = jac->y[i];
681       ilink->z = NULL;
682       /* compute scatter contexts needed by multiplicative versions and non-default splits */
683       PetscCall(VecScatterCreate(xtmp, ilink->is, jac->x[i], NULL, &ilink->sctx));
684       PetscCall(PetscObjectQuery((PetscObject)ilink->is, "nearnullspace", (PetscObject *)&sp));
685       if (sp) PetscCall(MatSetNearNullSpace(jac->pmat[i], sp));
686       ilink = ilink->next;
687     }
688     PetscCall(VecDestroy(&xtmp));
689   } else {
690     MatReuse scall;
691     if (pc->flag == DIFFERENT_NONZERO_PATTERN) {
692       for (i = 0; i < nsplit; i++) PetscCall(MatDestroy(&jac->pmat[i]));
693       scall = MAT_INITIAL_MATRIX;
694     } else scall = MAT_REUSE_MATRIX;
695 
696     for (i = 0; i < nsplit; i++) {
697       Mat pmat;
698 
699       /* Check for preconditioning matrix attached to IS */
700       PetscCall(PetscObjectQuery((PetscObject)ilink->is, "pmat", (PetscObject *)&pmat));
701       if (!pmat) PetscCall(MatCreateSubMatrix(pc->pmat, ilink->is, ilink->is_col, scall, &jac->pmat[i]));
702       ilink = ilink->next;
703     }
704   }
705   if (jac->diag_use_amat) {
706     ilink = jac->head;
707     if (!jac->mat) {
708       PetscCall(PetscMalloc1(nsplit, &jac->mat));
709       for (i = 0; i < nsplit; i++) {
710         PetscCall(MatCreateSubMatrix(pc->mat, ilink->is, ilink->is_col, MAT_INITIAL_MATRIX, &jac->mat[i]));
711         ilink = ilink->next;
712       }
713     } else {
714       MatReuse scall;
715       if (pc->flag == DIFFERENT_NONZERO_PATTERN) {
716         for (i = 0; i < nsplit; i++) PetscCall(MatDestroy(&jac->mat[i]));
717         scall = MAT_INITIAL_MATRIX;
718       } else scall = MAT_REUSE_MATRIX;
719 
720       for (i = 0; i < nsplit; i++) {
721         PetscCall(MatCreateSubMatrix(pc->mat, ilink->is, ilink->is_col, scall, &jac->mat[i]));
722         ilink = ilink->next;
723       }
724     }
725   } else {
726     jac->mat = jac->pmat;
727   }
728 
729   /* Check for null space attached to IS */
730   ilink = jac->head;
731   for (i = 0; i < nsplit; i++) {
732     MatNullSpace sp;
733 
734     PetscCall(PetscObjectQuery((PetscObject)ilink->is, "nullspace", (PetscObject *)&sp));
735     if (sp) PetscCall(MatSetNullSpace(jac->mat[i], sp));
736     ilink = ilink->next;
737   }
738 
739   if (jac->type != PC_COMPOSITE_ADDITIVE && jac->type != PC_COMPOSITE_SCHUR && jac->type != PC_COMPOSITE_GKB) {
740     /* extract the rows of the matrix associated with each field: used for efficient computation of residual inside algorithm */
741     /* FIXME: Can/should we reuse jac->mat whenever (jac->diag_use_amat) is true? */
742     ilink = jac->head;
743     if (nsplit == 2 && jac->type == PC_COMPOSITE_MULTIPLICATIVE) {
744       /* special case need where Afield[0] is not needed and only certain columns of Afield[1] are needed since update is only on those rows of the solution */
745       if (!jac->Afield) {
746         PetscCall(PetscCalloc1(nsplit, &jac->Afield));
747         if (jac->offdiag_use_amat) {
748           PetscCall(MatCreateSubMatrix(pc->mat, ilink->next->is, ilink->is, MAT_INITIAL_MATRIX, &jac->Afield[1]));
749         } else {
750           PetscCall(MatCreateSubMatrix(pc->pmat, ilink->next->is, ilink->is, MAT_INITIAL_MATRIX, &jac->Afield[1]));
751         }
752       } else {
753         MatReuse scall;
754 
755         if (pc->flag == DIFFERENT_NONZERO_PATTERN) {
756           PetscCall(MatDestroy(&jac->Afield[1]));
757           scall = MAT_INITIAL_MATRIX;
758         } else scall = MAT_REUSE_MATRIX;
759 
760         if (jac->offdiag_use_amat) {
761           PetscCall(MatCreateSubMatrix(pc->mat, ilink->next->is, ilink->is, scall, &jac->Afield[1]));
762         } else {
763           PetscCall(MatCreateSubMatrix(pc->pmat, ilink->next->is, ilink->is, scall, &jac->Afield[1]));
764         }
765       }
766     } else {
767       if (!jac->Afield) {
768         PetscCall(PetscMalloc1(nsplit, &jac->Afield));
769         for (i = 0; i < nsplit; i++) {
770           if (jac->offdiag_use_amat) {
771             PetscCall(MatCreateSubMatrix(pc->mat, ilink->is, NULL, MAT_INITIAL_MATRIX, &jac->Afield[i]));
772           } else {
773             PetscCall(MatCreateSubMatrix(pc->pmat, ilink->is, NULL, MAT_INITIAL_MATRIX, &jac->Afield[i]));
774           }
775           ilink = ilink->next;
776         }
777       } else {
778         MatReuse scall;
779         if (pc->flag == DIFFERENT_NONZERO_PATTERN) {
780           for (i = 0; i < nsplit; i++) PetscCall(MatDestroy(&jac->Afield[i]));
781           scall = MAT_INITIAL_MATRIX;
782         } else scall = MAT_REUSE_MATRIX;
783 
784         for (i = 0; i < nsplit; i++) {
785           if (jac->offdiag_use_amat) {
786             PetscCall(MatCreateSubMatrix(pc->mat, ilink->is, NULL, scall, &jac->Afield[i]));
787           } else {
788             PetscCall(MatCreateSubMatrix(pc->pmat, ilink->is, NULL, scall, &jac->Afield[i]));
789           }
790           ilink = ilink->next;
791         }
792       }
793     }
794   }
795 
796   if (jac->type == PC_COMPOSITE_SCHUR) {
797     IS          ccis;
798     PetscBool   isset, isspd;
799     PetscInt    rstart, rend;
800     char        lscname[256];
801     PetscObject LSC_L;
802     PetscBool   set, flg;
803 
804     PetscCheck(nsplit == 2, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_INCOMP, "To use Schur complement preconditioner you must have exactly 2 fields");
805 
806     /* If pc->mat is SPD, don't scale by -1 the Schur complement */
807     if (jac->schurscale == (PetscScalar)-1.0) {
808       PetscCall(MatIsSPDKnown(pc->pmat, &isset, &isspd));
809       jac->schurscale = (isset && isspd) ? 1.0 : -1.0;
810     }
811 
812     /* When extracting off-diagonal submatrices, we take complements from this range */
813     PetscCall(MatGetOwnershipRangeColumn(pc->mat, &rstart, &rend));
814     PetscCall(PetscObjectTypeCompareAny(jac->offdiag_use_amat ? (PetscObject)pc->mat : (PetscObject)pc->pmat, &flg, MATSEQSBAIJ, MATMPISBAIJ, ""));
815 
816     if (jac->schur) {
817       KSP      kspA = jac->head->ksp, kspInner = NULL, kspUpper = jac->kspupper;
818       MatReuse scall;
819 
820       if (pc->flag == DIFFERENT_NONZERO_PATTERN) {
821         scall = MAT_INITIAL_MATRIX;
822         PetscCall(MatDestroy(&jac->B));
823         PetscCall(MatDestroy(&jac->C));
824       } else scall = MAT_REUSE_MATRIX;
825 
826       PetscCall(MatSchurComplementGetKSP(jac->schur, &kspInner));
827       ilink = jac->head;
828       PetscCall(ISComplement(ilink->is_col, rstart, rend, &ccis));
829       if (jac->offdiag_use_amat) {
830         PetscCall(MatCreateSubMatrix(pc->mat, ilink->is, ccis, scall, &jac->B));
831       } else {
832         PetscCall(MatCreateSubMatrix(pc->pmat, ilink->is, ccis, scall, &jac->B));
833       }
834       PetscCall(ISDestroy(&ccis));
835       if (!flg) {
836         ilink = ilink->next;
837         PetscCall(ISComplement(ilink->is_col, rstart, rend, &ccis));
838         if (jac->offdiag_use_amat) {
839           PetscCall(MatCreateSubMatrix(pc->mat, ilink->is, ccis, scall, &jac->C));
840         } else {
841           PetscCall(MatCreateSubMatrix(pc->pmat, ilink->is, ccis, scall, &jac->C));
842         }
843         PetscCall(ISDestroy(&ccis));
844       } else {
845         PetscCall(MatIsHermitianKnown(jac->offdiag_use_amat ? pc->mat : pc->pmat, &set, &flg));
846         if (set && flg) PetscCall(MatCreateHermitianTranspose(jac->B, &jac->C));
847         else PetscCall(MatCreateTranspose(jac->B, &jac->C));
848       }
849       PetscCall(MatSchurComplementUpdateSubMatrices(jac->schur, jac->mat[0], jac->pmat[0], jac->B, jac->C, jac->mat[1]));
850       if (jac->schurpre == PC_FIELDSPLIT_SCHUR_PRE_SELFP) {
851         PetscCall(MatDestroy(&jac->schurp));
852         PetscCall(MatSchurComplementGetPmat(jac->schur, MAT_INITIAL_MATRIX, &jac->schurp));
853       }
854       if (kspA != kspInner) PetscCall(KSPSetOperators(kspA, jac->mat[0], jac->pmat[0]));
855       if (kspUpper != kspA) PetscCall(KSPSetOperators(kspUpper, jac->mat[0], jac->pmat[0]));
856       PetscCall(KSPSetOperators(jac->kspschur, jac->schur, FieldSplitSchurPre(jac)));
857     } else {
858       const char  *Dprefix;
859       char         schurprefix[256], schurmatprefix[256];
860       char         schurtestoption[256];
861       MatNullSpace sp;
862       KSP          kspt;
863 
864       /* extract the A01 and A10 matrices */
865       ilink = jac->head;
866       PetscCall(ISComplement(ilink->is_col, rstart, rend, &ccis));
867       if (jac->offdiag_use_amat) {
868         PetscCall(MatCreateSubMatrix(pc->mat, ilink->is, ccis, MAT_INITIAL_MATRIX, &jac->B));
869       } else {
870         PetscCall(MatCreateSubMatrix(pc->pmat, ilink->is, ccis, MAT_INITIAL_MATRIX, &jac->B));
871       }
872       PetscCall(ISDestroy(&ccis));
873       ilink = ilink->next;
874       if (!flg) {
875         PetscCall(ISComplement(ilink->is_col, rstart, rend, &ccis));
876         if (jac->offdiag_use_amat) {
877           PetscCall(MatCreateSubMatrix(pc->mat, ilink->is, ccis, MAT_INITIAL_MATRIX, &jac->C));
878         } else {
879           PetscCall(MatCreateSubMatrix(pc->pmat, ilink->is, ccis, MAT_INITIAL_MATRIX, &jac->C));
880         }
881         PetscCall(ISDestroy(&ccis));
882       } else {
883         PetscCall(MatIsHermitianKnown(jac->offdiag_use_amat ? pc->mat : pc->pmat, &set, &flg));
884         if (set && flg) PetscCall(MatCreateHermitianTranspose(jac->B, &jac->C));
885         else PetscCall(MatCreateTranspose(jac->B, &jac->C));
886       }
887       /* Use mat[0] (diagonal block of Amat) preconditioned by pmat[0] to define Schur complement */
888       PetscCall(MatCreate(((PetscObject)jac->mat[0])->comm, &jac->schur));
889       PetscCall(MatSetType(jac->schur, MATSCHURCOMPLEMENT));
890       PetscCall(MatSchurComplementSetSubMatrices(jac->schur, jac->mat[0], jac->pmat[0], jac->B, jac->C, jac->mat[1]));
891       PetscCall(PetscSNPrintf(schurmatprefix, sizeof(schurmatprefix), "%sfieldsplit_%s_", ((PetscObject)pc)->prefix ? ((PetscObject)pc)->prefix : "", ilink->splitname));
892       PetscCall(MatSetOptionsPrefix(jac->schur, schurmatprefix));
893       PetscCall(MatSchurComplementGetKSP(jac->schur, &kspt));
894       PetscCall(KSPSetOptionsPrefix(kspt, schurmatprefix));
895 
896       /* Note: this is not true in general */
897       PetscCall(MatGetNullSpace(jac->mat[1], &sp));
898       if (sp) PetscCall(MatSetNullSpace(jac->schur, sp));
899 
900       PetscCall(PetscSNPrintf(schurtestoption, sizeof(schurtestoption), "-fieldsplit_%s_inner_", ilink->splitname));
901       PetscCall(PetscOptionsFindPairPrefix_Private(((PetscObject)pc)->options, ((PetscObject)pc)->prefix, schurtestoption, NULL, &flg));
902       if (flg) {
903         DM  dmInner;
904         KSP kspInner;
905         PC  pcInner;
906 
907         PetscCall(MatSchurComplementGetKSP(jac->schur, &kspInner));
908         PetscCall(KSPReset(kspInner));
909         PetscCall(KSPSetOperators(kspInner, jac->mat[0], jac->pmat[0]));
910         PetscCall(PetscSNPrintf(schurprefix, sizeof(schurprefix), "%sfieldsplit_%s_inner_", ((PetscObject)pc)->prefix ? ((PetscObject)pc)->prefix : "", ilink->splitname));
911         /* Indent this deeper to emphasize the "inner" nature of this solver. */
912         PetscCall(PetscObjectIncrementTabLevel((PetscObject)kspInner, (PetscObject)pc, 2));
913         PetscCall(PetscObjectIncrementTabLevel((PetscObject)kspInner->pc, (PetscObject)pc, 2));
914         PetscCall(KSPSetOptionsPrefix(kspInner, schurprefix));
915 
916         /* Set DM for new solver */
917         PetscCall(KSPGetDM(jac->head->ksp, &dmInner));
918         PetscCall(KSPSetDM(kspInner, dmInner));
919         PetscCall(KSPSetDMActive(kspInner, PETSC_FALSE));
920 
921         /* Defaults to PCKSP as preconditioner */
922         PetscCall(KSPGetPC(kspInner, &pcInner));
923         PetscCall(PCSetType(pcInner, PCKSP));
924         PetscCall(PCKSPSetKSP(pcInner, jac->head->ksp));
925       } else {
926         /* Use the outer solver for the inner solve, but revert the KSPPREONLY from PCFieldSplitSetFields_FieldSplit or
927           * PCFieldSplitSetIS_FieldSplit. We don't want KSPPREONLY because it makes the Schur complement inexact,
928           * preventing Schur complement reduction to be an accurate solve. Usually when an iterative solver is used for
929           * S = D - C A_inner^{-1} B, we expect S to be defined using an accurate definition of A_inner^{-1}, so we make
930           * GMRES the default. Note that it is also common to use PREONLY for S, in which case S may not be used
931           * directly, and the user is responsible for setting an inexact method for fieldsplit's A^{-1}. */
932         PetscCall(KSPSetType(jac->head->ksp, KSPGMRES));
933         PetscCall(MatSchurComplementSetKSP(jac->schur, jac->head->ksp));
934       }
935       PetscCall(KSPSetOperators(jac->head->ksp, jac->mat[0], jac->pmat[0]));
936       PetscCall(KSPSetFromOptions(jac->head->ksp));
937       PetscCall(MatSetFromOptions(jac->schur));
938 
939       PetscCall(PetscObjectTypeCompare((PetscObject)jac->schur, MATSCHURCOMPLEMENT, &flg));
940       if (flg) { /* Need to do this otherwise PCSetUp_KSP will overwrite the amat of jac->head->ksp */
941         KSP kspInner;
942         PC  pcInner;
943 
944         PetscCall(MatSchurComplementGetKSP(jac->schur, &kspInner));
945         PetscCall(KSPGetPC(kspInner, &pcInner));
946         PetscCall(PetscObjectTypeCompare((PetscObject)pcInner, PCKSP, &flg));
947         if (flg) {
948           KSP ksp;
949 
950           PetscCall(PCKSPGetKSP(pcInner, &ksp));
951           if (ksp == jac->head->ksp) PetscCall(PCSetUseAmat(pcInner, PETSC_TRUE));
952         }
953       }
954       PetscCall(PetscSNPrintf(schurtestoption, sizeof(schurtestoption), "-fieldsplit_%s_upper_", ilink->splitname));
955       PetscCall(PetscOptionsFindPairPrefix_Private(((PetscObject)pc)->options, ((PetscObject)pc)->prefix, schurtestoption, NULL, &flg));
956       if (flg) {
957         DM dmInner;
958 
959         PetscCall(PetscSNPrintf(schurprefix, sizeof(schurprefix), "%sfieldsplit_%s_upper_", ((PetscObject)pc)->prefix ? ((PetscObject)pc)->prefix : "", ilink->splitname));
960         PetscCall(KSPCreate(PetscObjectComm((PetscObject)pc), &jac->kspupper));
961         PetscCall(KSPSetNestLevel(jac->kspupper, pc->kspnestlevel));
962         PetscCall(KSPSetErrorIfNotConverged(jac->kspupper, pc->erroriffailure));
963         PetscCall(KSPSetOptionsPrefix(jac->kspupper, schurprefix));
964         PetscCall(PetscObjectIncrementTabLevel((PetscObject)jac->kspupper, (PetscObject)pc, 1));
965         PetscCall(PetscObjectIncrementTabLevel((PetscObject)jac->kspupper->pc, (PetscObject)pc, 1));
966         PetscCall(KSPGetDM(jac->head->ksp, &dmInner));
967         PetscCall(KSPSetDM(jac->kspupper, dmInner));
968         PetscCall(KSPSetDMActive(jac->kspupper, PETSC_FALSE));
969         PetscCall(KSPSetFromOptions(jac->kspupper));
970         PetscCall(KSPSetOperators(jac->kspupper, jac->mat[0], jac->pmat[0]));
971         PetscCall(VecDuplicate(jac->head->x, &jac->head->z));
972       } else {
973         jac->kspupper = jac->head->ksp;
974         PetscCall(PetscObjectReference((PetscObject)jac->head->ksp));
975       }
976 
977       if (jac->schurpre == PC_FIELDSPLIT_SCHUR_PRE_SELFP) PetscCall(MatSchurComplementGetPmat(jac->schur, MAT_INITIAL_MATRIX, &jac->schurp));
978       PetscCall(KSPCreate(PetscObjectComm((PetscObject)pc), &jac->kspschur));
979       PetscCall(KSPSetNestLevel(jac->kspschur, pc->kspnestlevel));
980       PetscCall(KSPSetErrorIfNotConverged(jac->kspschur, pc->erroriffailure));
981       PetscCall(PetscObjectIncrementTabLevel((PetscObject)jac->kspschur, (PetscObject)pc, 1));
982       if (jac->schurpre == PC_FIELDSPLIT_SCHUR_PRE_SELF) {
983         PC pcschur;
984         PetscCall(KSPGetPC(jac->kspschur, &pcschur));
985         PetscCall(PCSetType(pcschur, PCNONE));
986         /* Note: This is bad if there exist preconditioners for MATSCHURCOMPLEMENT */
987       } else if (jac->schurpre == PC_FIELDSPLIT_SCHUR_PRE_FULL) {
988         PetscCall(MatSchurComplementComputeExplicitOperator(jac->schur, &jac->schur_user));
989       }
990       PetscCall(KSPSetOperators(jac->kspschur, jac->schur, FieldSplitSchurPre(jac)));
991       PetscCall(KSPGetOptionsPrefix(jac->head->next->ksp, &Dprefix));
992       PetscCall(KSPSetOptionsPrefix(jac->kspschur, Dprefix));
993       /* propagate DM */
994       {
995         DM sdm;
996         PetscCall(KSPGetDM(jac->head->next->ksp, &sdm));
997         if (sdm) {
998           PetscCall(KSPSetDM(jac->kspschur, sdm));
999           PetscCall(KSPSetDMActive(jac->kspschur, PETSC_FALSE));
1000         }
1001       }
1002       /* really want setfromoptions called in PCSetFromOptions_FieldSplit(), but it is not ready yet */
1003       /* need to call this every time, since the jac->kspschur is freshly created, otherwise its options never get set */
1004       PetscCall(KSPSetFromOptions(jac->kspschur));
1005     }
1006     PetscCall(MatAssemblyBegin(jac->schur, MAT_FINAL_ASSEMBLY));
1007     PetscCall(MatAssemblyEnd(jac->schur, MAT_FINAL_ASSEMBLY));
1008 
1009     /* HACK: special support to forward L and Lp matrices that might be used by PCLSC */
1010     PetscCall(PetscSNPrintf(lscname, sizeof(lscname), "%s_LSC_L", ilink->splitname));
1011     PetscCall(PetscObjectQuery((PetscObject)pc->mat, lscname, (PetscObject *)&LSC_L));
1012     if (!LSC_L) PetscCall(PetscObjectQuery((PetscObject)pc->pmat, lscname, (PetscObject *)&LSC_L));
1013     if (LSC_L) PetscCall(PetscObjectCompose((PetscObject)jac->schur, "LSC_L", (PetscObject)LSC_L));
1014     PetscCall(PetscSNPrintf(lscname, sizeof(lscname), "%s_LSC_Lp", ilink->splitname));
1015     PetscCall(PetscObjectQuery((PetscObject)pc->pmat, lscname, (PetscObject *)&LSC_L));
1016     if (!LSC_L) PetscCall(PetscObjectQuery((PetscObject)pc->mat, lscname, (PetscObject *)&LSC_L));
1017     if (LSC_L) PetscCall(PetscObjectCompose((PetscObject)jac->schur, "LSC_Lp", (PetscObject)LSC_L));
1018   } else if (jac->type == PC_COMPOSITE_GKB) {
1019     IS       ccis;
1020     PetscInt rstart, rend;
1021 
1022     PetscCheck(nsplit == 2, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_INCOMP, "To use GKB preconditioner you must have exactly 2 fields");
1023 
1024     ilink = jac->head;
1025 
1026     /* When extracting off-diagonal submatrices, we take complements from this range */
1027     PetscCall(MatGetOwnershipRangeColumn(pc->mat, &rstart, &rend));
1028 
1029     PetscCall(ISComplement(ilink->is_col, rstart, rend, &ccis));
1030     if (jac->offdiag_use_amat) {
1031       PetscCall(MatCreateSubMatrix(pc->mat, ilink->is, ccis, MAT_INITIAL_MATRIX, &jac->B));
1032     } else {
1033       PetscCall(MatCreateSubMatrix(pc->pmat, ilink->is, ccis, MAT_INITIAL_MATRIX, &jac->B));
1034     }
1035     PetscCall(ISDestroy(&ccis));
1036     /* Create work vectors for GKB algorithm */
1037     PetscCall(VecDuplicate(ilink->x, &jac->u));
1038     PetscCall(VecDuplicate(ilink->x, &jac->Hu));
1039     PetscCall(VecDuplicate(ilink->x, &jac->w2));
1040     ilink = ilink->next;
1041     PetscCall(ISComplement(ilink->is_col, rstart, rend, &ccis));
1042     if (jac->offdiag_use_amat) {
1043       PetscCall(MatCreateSubMatrix(pc->mat, ilink->is, ccis, MAT_INITIAL_MATRIX, &jac->C));
1044     } else {
1045       PetscCall(MatCreateSubMatrix(pc->pmat, ilink->is, ccis, MAT_INITIAL_MATRIX, &jac->C));
1046     }
1047     PetscCall(ISDestroy(&ccis));
1048     /* Create work vectors for GKB algorithm */
1049     PetscCall(VecDuplicate(ilink->x, &jac->v));
1050     PetscCall(VecDuplicate(ilink->x, &jac->d));
1051     PetscCall(VecDuplicate(ilink->x, &jac->w1));
1052     PetscCall(MatGolubKahanComputeExplicitOperator(jac->mat[0], jac->B, jac->C, &jac->H, jac->gkbnu));
1053     PetscCall(PetscCalloc1(jac->gkbdelay, &jac->vecz));
1054 
1055     ilink = jac->head;
1056     PetscCall(KSPSetOperators(ilink->ksp, jac->H, jac->H));
1057     if (!jac->suboptionsset) PetscCall(KSPSetFromOptions(ilink->ksp));
1058     /* Create gkb_monitor context */
1059     if (jac->gkbmonitor) {
1060       PetscInt tablevel;
1061       PetscCall(PetscViewerCreate(PETSC_COMM_WORLD, &jac->gkbviewer));
1062       PetscCall(PetscViewerSetType(jac->gkbviewer, PETSCVIEWERASCII));
1063       PetscCall(PetscObjectGetTabLevel((PetscObject)ilink->ksp, &tablevel));
1064       PetscCall(PetscViewerASCIISetTab(jac->gkbviewer, tablevel));
1065       PetscCall(PetscObjectIncrementTabLevel((PetscObject)ilink->ksp, (PetscObject)ilink->ksp, 1));
1066     }
1067   } else {
1068     /* set up the individual splits' PCs */
1069     i     = 0;
1070     ilink = jac->head;
1071     while (ilink) {
1072       PetscCall(KSPSetOperators(ilink->ksp, jac->mat[i], jac->pmat[i]));
1073       /* really want setfromoptions called in PCSetFromOptions_FieldSplit(), but it is not ready yet */
1074       if (!jac->suboptionsset) PetscCall(KSPSetFromOptions(ilink->ksp));
1075       i++;
1076       ilink = ilink->next;
1077     }
1078   }
1079 
1080   /* Set coordinates to the sub PC objects whenever these are set */
1081   if (jac->coordinates_set) {
1082     PC pc_coords;
1083     if (jac->type == PC_COMPOSITE_SCHUR) {
1084       // Head is first block.
1085       PetscCall(KSPGetPC(jac->head->ksp, &pc_coords));
1086       PetscCall(PCSetCoordinates(pc_coords, jac->head->dim, jac->head->ndofs, jac->head->coords));
1087       // Second one is Schur block, but its KSP object is in kspschur.
1088       PetscCall(KSPGetPC(jac->kspschur, &pc_coords));
1089       PetscCall(PCSetCoordinates(pc_coords, jac->head->next->dim, jac->head->next->ndofs, jac->head->next->coords));
1090     } else if (jac->type == PC_COMPOSITE_GKB) {
1091       PetscCall(PetscInfo(pc, "Warning: Setting coordinates does nothing for the GKB Fieldpslit preconditioner\n"));
1092     } else {
1093       ilink = jac->head;
1094       while (ilink) {
1095         PetscCall(KSPGetPC(ilink->ksp, &pc_coords));
1096         PetscCall(PCSetCoordinates(pc_coords, ilink->dim, ilink->ndofs, ilink->coords));
1097         ilink = ilink->next;
1098       }
1099     }
1100   }
1101 
1102   jac->suboptionsset = PETSC_TRUE;
1103   PetscFunctionReturn(PETSC_SUCCESS);
1104 }
1105 
1106 #define FieldSplitSplitSolveAdd(ilink, xx, yy) \
1107   ((PetscErrorCode)(VecScatterBegin(ilink->sctx, xx, ilink->x, INSERT_VALUES, SCATTER_FORWARD) || VecScatterEnd(ilink->sctx, xx, ilink->x, INSERT_VALUES, SCATTER_FORWARD) || PetscLogEventBegin(ilink->event, ilink->ksp, ilink->x, ilink->y, NULL) || \
1108                     KSPSolve(ilink->ksp, ilink->x, ilink->y) || KSPCheckSolve(ilink->ksp, pc, ilink->y) || PetscLogEventEnd(ilink->event, ilink->ksp, ilink->x, ilink->y, NULL) || VecScatterBegin(ilink->sctx, ilink->y, yy, ADD_VALUES, SCATTER_REVERSE) || \
1109                     VecScatterEnd(ilink->sctx, ilink->y, yy, ADD_VALUES, SCATTER_REVERSE)))
1110 
1111 static PetscErrorCode PCApply_FieldSplit_Schur(PC pc, Vec x, Vec y)
1112 {
1113   PC_FieldSplit    *jac    = (PC_FieldSplit *)pc->data;
1114   PC_FieldSplitLink ilinkA = jac->head, ilinkD = ilinkA->next;
1115   KSP               kspA = ilinkA->ksp, kspLower = kspA, kspUpper = jac->kspupper;
1116 
1117   PetscFunctionBegin;
1118   switch (jac->schurfactorization) {
1119   case PC_FIELDSPLIT_SCHUR_FACT_DIAG:
1120     /* [A00 0; 0 -S], positive definite, suitable for MINRES */
1121     PetscCall(VecScatterBegin(ilinkA->sctx, x, ilinkA->x, INSERT_VALUES, SCATTER_FORWARD));
1122     PetscCall(VecScatterBegin(ilinkD->sctx, x, ilinkD->x, INSERT_VALUES, SCATTER_FORWARD));
1123     PetscCall(VecScatterEnd(ilinkA->sctx, x, ilinkA->x, INSERT_VALUES, SCATTER_FORWARD));
1124     PetscCall(PetscLogEventBegin(ilinkA->event, kspA, ilinkA->x, ilinkA->y, NULL));
1125     PetscCall(KSPSolve(kspA, ilinkA->x, ilinkA->y));
1126     PetscCall(KSPCheckSolve(kspA, pc, ilinkA->y));
1127     PetscCall(PetscLogEventEnd(ilinkA->event, kspA, ilinkA->x, ilinkA->y, NULL));
1128     PetscCall(VecScatterBegin(ilinkA->sctx, ilinkA->y, y, INSERT_VALUES, SCATTER_REVERSE));
1129     PetscCall(VecScatterEnd(ilinkD->sctx, x, ilinkD->x, INSERT_VALUES, SCATTER_FORWARD));
1130     PetscCall(PetscLogEventBegin(KSP_Solve_FS_S, jac->kspschur, ilinkD->x, ilinkD->y, NULL));
1131     PetscCall(KSPSolve(jac->kspschur, ilinkD->x, ilinkD->y));
1132     PetscCall(KSPCheckSolve(jac->kspschur, pc, ilinkD->y));
1133     PetscCall(PetscLogEventEnd(KSP_Solve_FS_S, jac->kspschur, ilinkD->x, ilinkD->y, NULL));
1134     PetscCall(VecScale(ilinkD->y, jac->schurscale));
1135     PetscCall(VecScatterEnd(ilinkA->sctx, ilinkA->y, y, INSERT_VALUES, SCATTER_REVERSE));
1136     PetscCall(VecScatterBegin(ilinkD->sctx, ilinkD->y, y, INSERT_VALUES, SCATTER_REVERSE));
1137     PetscCall(VecScatterEnd(ilinkD->sctx, ilinkD->y, y, INSERT_VALUES, SCATTER_REVERSE));
1138     break;
1139   case PC_FIELDSPLIT_SCHUR_FACT_LOWER:
1140     /* [A00 0; A10 S], suitable for left preconditioning */
1141     PetscCall(VecScatterBegin(ilinkA->sctx, x, ilinkA->x, INSERT_VALUES, SCATTER_FORWARD));
1142     PetscCall(VecScatterEnd(ilinkA->sctx, x, ilinkA->x, INSERT_VALUES, SCATTER_FORWARD));
1143     PetscCall(PetscLogEventBegin(ilinkA->event, kspA, ilinkA->x, ilinkA->y, NULL));
1144     PetscCall(KSPSolve(kspA, ilinkA->x, ilinkA->y));
1145     PetscCall(KSPCheckSolve(kspA, pc, ilinkA->y));
1146     PetscCall(PetscLogEventEnd(ilinkA->event, kspA, ilinkA->x, ilinkA->y, NULL));
1147     PetscCall(MatMult(jac->C, ilinkA->y, ilinkD->x));
1148     PetscCall(VecScale(ilinkD->x, -1.));
1149     PetscCall(VecScatterBegin(ilinkD->sctx, x, ilinkD->x, ADD_VALUES, SCATTER_FORWARD));
1150     PetscCall(VecScatterBegin(ilinkA->sctx, ilinkA->y, y, INSERT_VALUES, SCATTER_REVERSE));
1151     PetscCall(VecScatterEnd(ilinkD->sctx, x, ilinkD->x, ADD_VALUES, SCATTER_FORWARD));
1152     PetscCall(PetscLogEventBegin(KSP_Solve_FS_S, jac->kspschur, ilinkD->x, ilinkD->y, NULL));
1153     PetscCall(KSPSolve(jac->kspschur, ilinkD->x, ilinkD->y));
1154     PetscCall(KSPCheckSolve(jac->kspschur, pc, ilinkD->y));
1155     PetscCall(PetscLogEventEnd(KSP_Solve_FS_S, jac->kspschur, ilinkD->x, ilinkD->y, NULL));
1156     PetscCall(VecScatterEnd(ilinkA->sctx, ilinkA->y, y, INSERT_VALUES, SCATTER_REVERSE));
1157     PetscCall(VecScatterBegin(ilinkD->sctx, ilinkD->y, y, INSERT_VALUES, SCATTER_REVERSE));
1158     PetscCall(VecScatterEnd(ilinkD->sctx, ilinkD->y, y, INSERT_VALUES, SCATTER_REVERSE));
1159     break;
1160   case PC_FIELDSPLIT_SCHUR_FACT_UPPER:
1161     /* [A00 A01; 0 S], suitable for right preconditioning */
1162     PetscCall(VecScatterBegin(ilinkD->sctx, x, ilinkD->x, INSERT_VALUES, SCATTER_FORWARD));
1163     PetscCall(VecScatterEnd(ilinkD->sctx, x, ilinkD->x, INSERT_VALUES, SCATTER_FORWARD));
1164     PetscCall(PetscLogEventBegin(KSP_Solve_FS_S, jac->kspschur, ilinkD->x, ilinkD->y, NULL));
1165     PetscCall(KSPSolve(jac->kspschur, ilinkD->x, ilinkD->y));
1166     PetscCall(KSPCheckSolve(jac->kspschur, pc, ilinkD->y));
1167     PetscCall(PetscLogEventEnd(KSP_Solve_FS_S, jac->kspschur, ilinkD->x, ilinkD->y, NULL));
1168     PetscCall(MatMult(jac->B, ilinkD->y, ilinkA->x));
1169     PetscCall(VecScale(ilinkA->x, -1.));
1170     PetscCall(VecScatterBegin(ilinkA->sctx, x, ilinkA->x, ADD_VALUES, SCATTER_FORWARD));
1171     PetscCall(VecScatterBegin(ilinkD->sctx, ilinkD->y, y, INSERT_VALUES, SCATTER_REVERSE));
1172     PetscCall(VecScatterEnd(ilinkA->sctx, x, ilinkA->x, ADD_VALUES, SCATTER_FORWARD));
1173     PetscCall(PetscLogEventBegin(ilinkA->event, kspA, ilinkA->x, ilinkA->y, NULL));
1174     PetscCall(KSPSolve(kspA, ilinkA->x, ilinkA->y));
1175     PetscCall(KSPCheckSolve(kspA, pc, ilinkA->y));
1176     PetscCall(PetscLogEventEnd(ilinkA->event, kspA, ilinkA->x, ilinkA->y, NULL));
1177     PetscCall(VecScatterEnd(ilinkD->sctx, ilinkD->y, y, INSERT_VALUES, SCATTER_REVERSE));
1178     PetscCall(VecScatterBegin(ilinkA->sctx, ilinkA->y, y, INSERT_VALUES, SCATTER_REVERSE));
1179     PetscCall(VecScatterEnd(ilinkA->sctx, ilinkA->y, y, INSERT_VALUES, SCATTER_REVERSE));
1180     break;
1181   case PC_FIELDSPLIT_SCHUR_FACT_FULL:
1182     /* [1 0; A10 A00^{-1} 1] [A00 0; 0 S] [1 A00^{-1}A01; 0 1] */
1183     PetscCall(VecScatterBegin(ilinkA->sctx, x, ilinkA->x, INSERT_VALUES, SCATTER_FORWARD));
1184     PetscCall(VecScatterEnd(ilinkA->sctx, x, ilinkA->x, INSERT_VALUES, SCATTER_FORWARD));
1185     PetscCall(PetscLogEventBegin(KSP_Solve_FS_L, kspLower, ilinkA->x, ilinkA->y, NULL));
1186     PetscCall(KSPSolve(kspLower, ilinkA->x, ilinkA->y));
1187     PetscCall(KSPCheckSolve(kspLower, pc, ilinkA->y));
1188     PetscCall(PetscLogEventEnd(KSP_Solve_FS_L, kspLower, ilinkA->x, ilinkA->y, NULL));
1189     PetscCall(MatMult(jac->C, ilinkA->y, ilinkD->x));
1190     PetscCall(VecScale(ilinkD->x, -1.0));
1191     PetscCall(VecScatterBegin(ilinkD->sctx, x, ilinkD->x, ADD_VALUES, SCATTER_FORWARD));
1192     PetscCall(VecScatterEnd(ilinkD->sctx, x, ilinkD->x, ADD_VALUES, SCATTER_FORWARD));
1193 
1194     PetscCall(PetscLogEventBegin(KSP_Solve_FS_S, jac->kspschur, ilinkD->x, ilinkD->y, NULL));
1195     PetscCall(KSPSolve(jac->kspschur, ilinkD->x, ilinkD->y));
1196     PetscCall(KSPCheckSolve(jac->kspschur, pc, ilinkD->y));
1197     PetscCall(PetscLogEventEnd(KSP_Solve_FS_S, jac->kspschur, ilinkD->x, ilinkD->y, NULL));
1198     PetscCall(VecScatterBegin(ilinkD->sctx, ilinkD->y, y, INSERT_VALUES, SCATTER_REVERSE));
1199 
1200     if (kspUpper == kspA) {
1201       PetscCall(MatMult(jac->B, ilinkD->y, ilinkA->y));
1202       PetscCall(VecAXPY(ilinkA->x, -1.0, ilinkA->y));
1203       PetscCall(PetscLogEventBegin(ilinkA->event, kspA, ilinkA->x, ilinkA->y, NULL));
1204       PetscCall(KSPSolve(kspA, ilinkA->x, ilinkA->y));
1205       PetscCall(KSPCheckSolve(kspA, pc, ilinkA->y));
1206       PetscCall(PetscLogEventEnd(ilinkA->event, kspA, ilinkA->x, ilinkA->y, NULL));
1207     } else {
1208       PetscCall(PetscLogEventBegin(ilinkA->event, kspA, ilinkA->x, ilinkA->y, NULL));
1209       PetscCall(KSPSolve(kspA, ilinkA->x, ilinkA->y));
1210       PetscCall(KSPCheckSolve(kspA, pc, ilinkA->y));
1211       PetscCall(MatMult(jac->B, ilinkD->y, ilinkA->x));
1212       PetscCall(PetscLogEventBegin(KSP_Solve_FS_U, kspUpper, ilinkA->x, ilinkA->z, NULL));
1213       PetscCall(KSPSolve(kspUpper, ilinkA->x, ilinkA->z));
1214       PetscCall(KSPCheckSolve(kspUpper, pc, ilinkA->z));
1215       PetscCall(PetscLogEventEnd(KSP_Solve_FS_U, kspUpper, ilinkA->x, ilinkA->z, NULL));
1216       PetscCall(VecAXPY(ilinkA->y, -1.0, ilinkA->z));
1217     }
1218     PetscCall(VecScatterEnd(ilinkD->sctx, ilinkD->y, y, INSERT_VALUES, SCATTER_REVERSE));
1219     PetscCall(VecScatterBegin(ilinkA->sctx, ilinkA->y, y, INSERT_VALUES, SCATTER_REVERSE));
1220     PetscCall(VecScatterEnd(ilinkA->sctx, ilinkA->y, y, INSERT_VALUES, SCATTER_REVERSE));
1221   }
1222   PetscFunctionReturn(PETSC_SUCCESS);
1223 }
1224 
1225 static PetscErrorCode PCApplyTranspose_FieldSplit_Schur(PC pc, Vec x, Vec y)
1226 {
1227   PC_FieldSplit    *jac    = (PC_FieldSplit *)pc->data;
1228   PC_FieldSplitLink ilinkA = jac->head, ilinkD = ilinkA->next;
1229   KSP               kspA = ilinkA->ksp, kspLower = kspA, kspUpper = jac->kspupper;
1230 
1231   PetscFunctionBegin;
1232   switch (jac->schurfactorization) {
1233   case PC_FIELDSPLIT_SCHUR_FACT_DIAG:
1234     /* [A00 0; 0 -S], positive definite, suitable for MINRES */
1235     PetscCall(VecScatterBegin(ilinkA->sctx, x, ilinkA->x, INSERT_VALUES, SCATTER_FORWARD));
1236     PetscCall(VecScatterBegin(ilinkD->sctx, x, ilinkD->x, INSERT_VALUES, SCATTER_FORWARD));
1237     PetscCall(VecScatterEnd(ilinkA->sctx, x, ilinkA->x, INSERT_VALUES, SCATTER_FORWARD));
1238     PetscCall(PetscLogEventBegin(ilinkA->event, kspA, ilinkA->x, ilinkA->y, NULL));
1239     PetscCall(KSPSolveTranspose(kspA, ilinkA->x, ilinkA->y));
1240     PetscCall(KSPCheckSolve(kspA, pc, ilinkA->y));
1241     PetscCall(PetscLogEventEnd(ilinkA->event, kspA, ilinkA->x, ilinkA->y, NULL));
1242     PetscCall(VecScatterBegin(ilinkA->sctx, ilinkA->y, y, INSERT_VALUES, SCATTER_REVERSE));
1243     PetscCall(VecScatterEnd(ilinkD->sctx, x, ilinkD->x, INSERT_VALUES, SCATTER_FORWARD));
1244     PetscCall(PetscLogEventBegin(KSP_Solve_FS_S, jac->kspschur, ilinkD->x, ilinkD->y, NULL));
1245     PetscCall(KSPSolveTranspose(jac->kspschur, ilinkD->x, ilinkD->y));
1246     PetscCall(KSPCheckSolve(jac->kspschur, pc, ilinkD->y));
1247     PetscCall(PetscLogEventEnd(KSP_Solve_FS_S, jac->kspschur, ilinkD->x, ilinkD->y, NULL));
1248     PetscCall(VecScale(ilinkD->y, jac->schurscale));
1249     PetscCall(VecScatterEnd(ilinkA->sctx, ilinkA->y, y, INSERT_VALUES, SCATTER_REVERSE));
1250     PetscCall(VecScatterBegin(ilinkD->sctx, ilinkD->y, y, INSERT_VALUES, SCATTER_REVERSE));
1251     PetscCall(VecScatterEnd(ilinkD->sctx, ilinkD->y, y, INSERT_VALUES, SCATTER_REVERSE));
1252     break;
1253   case PC_FIELDSPLIT_SCHUR_FACT_UPPER:
1254     PetscCall(VecScatterBegin(ilinkA->sctx, x, ilinkA->x, INSERT_VALUES, SCATTER_FORWARD));
1255     PetscCall(VecScatterEnd(ilinkA->sctx, x, ilinkA->x, INSERT_VALUES, SCATTER_FORWARD));
1256     PetscCall(PetscLogEventBegin(ilinkA->event, kspA, ilinkA->x, ilinkA->y, NULL));
1257     PetscCall(KSPSolveTranspose(kspA, ilinkA->x, ilinkA->y));
1258     PetscCall(KSPCheckSolve(kspA, pc, ilinkA->y));
1259     PetscCall(PetscLogEventEnd(ilinkA->event, kspA, ilinkA->x, ilinkA->y, NULL));
1260     PetscCall(MatMultTranspose(jac->B, ilinkA->y, ilinkD->x));
1261     PetscCall(VecScale(ilinkD->x, -1.));
1262     PetscCall(VecScatterBegin(ilinkD->sctx, x, ilinkD->x, ADD_VALUES, SCATTER_FORWARD));
1263     PetscCall(VecScatterBegin(ilinkA->sctx, ilinkA->y, y, INSERT_VALUES, SCATTER_REVERSE));
1264     PetscCall(VecScatterEnd(ilinkD->sctx, x, ilinkD->x, ADD_VALUES, SCATTER_FORWARD));
1265     PetscCall(PetscLogEventBegin(KSP_Solve_FS_S, jac->kspschur, ilinkD->x, ilinkD->y, NULL));
1266     PetscCall(KSPSolveTranspose(jac->kspschur, ilinkD->x, ilinkD->y));
1267     PetscCall(KSPCheckSolve(jac->kspschur, pc, ilinkD->y));
1268     PetscCall(PetscLogEventEnd(KSP_Solve_FS_S, jac->kspschur, ilinkD->x, ilinkD->y, NULL));
1269     PetscCall(VecScatterEnd(ilinkA->sctx, ilinkA->y, y, INSERT_VALUES, SCATTER_REVERSE));
1270     PetscCall(VecScatterBegin(ilinkD->sctx, ilinkD->y, y, INSERT_VALUES, SCATTER_REVERSE));
1271     PetscCall(VecScatterEnd(ilinkD->sctx, ilinkD->y, y, INSERT_VALUES, SCATTER_REVERSE));
1272     break;
1273   case PC_FIELDSPLIT_SCHUR_FACT_LOWER:
1274     PetscCall(VecScatterBegin(ilinkD->sctx, x, ilinkD->x, INSERT_VALUES, SCATTER_FORWARD));
1275     PetscCall(VecScatterEnd(ilinkD->sctx, x, ilinkD->x, INSERT_VALUES, SCATTER_FORWARD));
1276     PetscCall(PetscLogEventBegin(KSP_Solve_FS_S, jac->kspschur, ilinkD->x, ilinkD->y, NULL));
1277     PetscCall(KSPSolveTranspose(jac->kspschur, ilinkD->x, ilinkD->y));
1278     PetscCall(KSPCheckSolve(jac->kspschur, pc, ilinkD->y));
1279     PetscCall(PetscLogEventEnd(KSP_Solve_FS_S, jac->kspschur, ilinkD->x, ilinkD->y, NULL));
1280     PetscCall(MatMultTranspose(jac->C, ilinkD->y, ilinkA->x));
1281     PetscCall(VecScale(ilinkA->x, -1.));
1282     PetscCall(VecScatterBegin(ilinkA->sctx, x, ilinkA->x, ADD_VALUES, SCATTER_FORWARD));
1283     PetscCall(VecScatterBegin(ilinkD->sctx, ilinkD->y, y, INSERT_VALUES, SCATTER_REVERSE));
1284     PetscCall(VecScatterEnd(ilinkA->sctx, x, ilinkA->x, ADD_VALUES, SCATTER_FORWARD));
1285     PetscCall(PetscLogEventBegin(ilinkA->event, kspA, ilinkA->x, ilinkA->y, NULL));
1286     PetscCall(KSPSolveTranspose(kspA, ilinkA->x, ilinkA->y));
1287     PetscCall(KSPCheckSolve(kspA, pc, ilinkA->y));
1288     PetscCall(PetscLogEventEnd(ilinkA->event, kspA, ilinkA->x, ilinkA->y, NULL));
1289     PetscCall(VecScatterEnd(ilinkD->sctx, ilinkD->y, y, INSERT_VALUES, SCATTER_REVERSE));
1290     PetscCall(VecScatterBegin(ilinkA->sctx, ilinkA->y, y, INSERT_VALUES, SCATTER_REVERSE));
1291     PetscCall(VecScatterEnd(ilinkA->sctx, ilinkA->y, y, INSERT_VALUES, SCATTER_REVERSE));
1292     break;
1293   case PC_FIELDSPLIT_SCHUR_FACT_FULL:
1294     PetscCall(VecScatterBegin(ilinkA->sctx, x, ilinkA->x, INSERT_VALUES, SCATTER_FORWARD));
1295     PetscCall(VecScatterEnd(ilinkA->sctx, x, ilinkA->x, INSERT_VALUES, SCATTER_FORWARD));
1296     PetscCall(PetscLogEventBegin(KSP_Solve_FS_U, kspUpper, ilinkA->x, ilinkA->y, NULL));
1297     PetscCall(KSPSolveTranspose(kspUpper, ilinkA->x, ilinkA->y));
1298     PetscCall(KSPCheckSolve(kspUpper, pc, ilinkA->y));
1299     PetscCall(PetscLogEventEnd(KSP_Solve_FS_U, kspUpper, ilinkA->x, ilinkA->y, NULL));
1300     PetscCall(MatMultTranspose(jac->B, ilinkA->y, ilinkD->x));
1301     PetscCall(VecScale(ilinkD->x, -1.0));
1302     PetscCall(VecScatterBegin(ilinkD->sctx, x, ilinkD->x, ADD_VALUES, SCATTER_FORWARD));
1303     PetscCall(VecScatterEnd(ilinkD->sctx, x, ilinkD->x, ADD_VALUES, SCATTER_FORWARD));
1304 
1305     PetscCall(PetscLogEventBegin(KSP_Solve_FS_S, jac->kspschur, ilinkD->x, ilinkD->y, NULL));
1306     PetscCall(KSPSolveTranspose(jac->kspschur, ilinkD->x, ilinkD->y));
1307     PetscCall(KSPCheckSolve(jac->kspschur, pc, ilinkD->y));
1308     PetscCall(PetscLogEventEnd(KSP_Solve_FS_S, jac->kspschur, ilinkD->x, ilinkD->y, NULL));
1309     PetscCall(VecScatterBegin(ilinkD->sctx, ilinkD->y, y, INSERT_VALUES, SCATTER_REVERSE));
1310 
1311     if (kspLower == kspA) {
1312       PetscCall(MatMultTranspose(jac->C, ilinkD->y, ilinkA->y));
1313       PetscCall(VecAXPY(ilinkA->x, -1.0, ilinkA->y));
1314       PetscCall(PetscLogEventBegin(ilinkA->event, kspA, ilinkA->x, ilinkA->y, NULL));
1315       PetscCall(KSPSolveTranspose(kspA, ilinkA->x, ilinkA->y));
1316       PetscCall(KSPCheckSolve(kspA, pc, ilinkA->y));
1317       PetscCall(PetscLogEventEnd(ilinkA->event, kspA, ilinkA->x, ilinkA->y, NULL));
1318     } else {
1319       PetscCall(PetscLogEventBegin(ilinkA->event, kspA, ilinkA->x, ilinkA->y, NULL));
1320       PetscCall(KSPSolveTranspose(kspA, ilinkA->x, ilinkA->y));
1321       PetscCall(KSPCheckSolve(kspA, pc, ilinkA->y));
1322       PetscCall(MatMultTranspose(jac->C, ilinkD->y, ilinkA->x));
1323       PetscCall(PetscLogEventBegin(KSP_Solve_FS_L, kspLower, ilinkA->x, ilinkA->z, NULL));
1324       PetscCall(KSPSolveTranspose(kspLower, ilinkA->x, ilinkA->z));
1325       PetscCall(KSPCheckSolve(kspLower, pc, ilinkA->z));
1326       PetscCall(PetscLogEventEnd(KSP_Solve_FS_L, kspLower, ilinkA->x, ilinkA->z, NULL));
1327       PetscCall(VecAXPY(ilinkA->y, -1.0, ilinkA->z));
1328     }
1329     PetscCall(VecScatterEnd(ilinkD->sctx, ilinkD->y, y, INSERT_VALUES, SCATTER_REVERSE));
1330     PetscCall(VecScatterBegin(ilinkA->sctx, ilinkA->y, y, INSERT_VALUES, SCATTER_REVERSE));
1331     PetscCall(VecScatterEnd(ilinkA->sctx, ilinkA->y, y, INSERT_VALUES, SCATTER_REVERSE));
1332   }
1333   PetscFunctionReturn(PETSC_SUCCESS);
1334 }
1335 
1336 static PetscErrorCode PCApply_FieldSplit(PC pc, Vec x, Vec y)
1337 {
1338   PC_FieldSplit    *jac   = (PC_FieldSplit *)pc->data;
1339   PC_FieldSplitLink ilink = jac->head;
1340   PetscInt          cnt, bs;
1341 
1342   PetscFunctionBegin;
1343   if (jac->type == PC_COMPOSITE_ADDITIVE) {
1344     if (jac->defaultsplit) {
1345       PetscCall(VecGetBlockSize(x, &bs));
1346       PetscCheck(jac->bs <= 0 || bs == jac->bs, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_WRONGSTATE, "Blocksize of x vector %" PetscInt_FMT " does not match fieldsplit blocksize %" PetscInt_FMT, bs, jac->bs);
1347       PetscCall(VecGetBlockSize(y, &bs));
1348       PetscCheck(jac->bs <= 0 || bs == jac->bs, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_WRONGSTATE, "Blocksize of y vector %" PetscInt_FMT " does not match fieldsplit blocksize %" PetscInt_FMT, bs, jac->bs);
1349       PetscCall(VecStrideGatherAll(x, jac->x, INSERT_VALUES));
1350       while (ilink) {
1351         PetscCall(PetscLogEventBegin(ilink->event, ilink->ksp, ilink->x, ilink->y, NULL));
1352         PetscCall(KSPSolve(ilink->ksp, ilink->x, ilink->y));
1353         PetscCall(KSPCheckSolve(ilink->ksp, pc, ilink->y));
1354         PetscCall(PetscLogEventEnd(ilink->event, ilink->ksp, ilink->x, ilink->y, NULL));
1355         ilink = ilink->next;
1356       }
1357       PetscCall(VecStrideScatterAll(jac->y, y, INSERT_VALUES));
1358     } else {
1359       PetscCall(VecSet(y, 0.0));
1360       while (ilink) {
1361         PetscCall(FieldSplitSplitSolveAdd(ilink, x, y));
1362         ilink = ilink->next;
1363       }
1364     }
1365   } else if (jac->type == PC_COMPOSITE_MULTIPLICATIVE && jac->nsplits == 2) {
1366     PetscCall(VecSet(y, 0.0));
1367     /* solve on first block for first block variables */
1368     PetscCall(VecScatterBegin(ilink->sctx, x, ilink->x, INSERT_VALUES, SCATTER_FORWARD));
1369     PetscCall(VecScatterEnd(ilink->sctx, x, ilink->x, INSERT_VALUES, SCATTER_FORWARD));
1370     PetscCall(PetscLogEventBegin(ilink->event, ilink->ksp, ilink->x, ilink->y, NULL));
1371     PetscCall(KSPSolve(ilink->ksp, ilink->x, ilink->y));
1372     PetscCall(KSPCheckSolve(ilink->ksp, pc, ilink->y));
1373     PetscCall(PetscLogEventEnd(ilink->event, ilink->ksp, ilink->x, ilink->y, NULL));
1374     PetscCall(VecScatterBegin(ilink->sctx, ilink->y, y, ADD_VALUES, SCATTER_REVERSE));
1375     PetscCall(VecScatterEnd(ilink->sctx, ilink->y, y, ADD_VALUES, SCATTER_REVERSE));
1376 
1377     /* compute the residual only onto second block variables using first block variables */
1378     PetscCall(MatMult(jac->Afield[1], ilink->y, ilink->next->x));
1379     ilink = ilink->next;
1380     PetscCall(VecScale(ilink->x, -1.0));
1381     PetscCall(VecScatterBegin(ilink->sctx, x, ilink->x, ADD_VALUES, SCATTER_FORWARD));
1382     PetscCall(VecScatterEnd(ilink->sctx, x, ilink->x, ADD_VALUES, SCATTER_FORWARD));
1383 
1384     /* solve on second block variables */
1385     PetscCall(PetscLogEventBegin(ilink->event, ilink->ksp, ilink->x, ilink->y, NULL));
1386     PetscCall(KSPSolve(ilink->ksp, ilink->x, ilink->y));
1387     PetscCall(KSPCheckSolve(ilink->ksp, pc, ilink->y));
1388     PetscCall(PetscLogEventEnd(ilink->event, ilink->ksp, ilink->x, ilink->y, NULL));
1389     PetscCall(VecScatterBegin(ilink->sctx, ilink->y, y, ADD_VALUES, SCATTER_REVERSE));
1390     PetscCall(VecScatterEnd(ilink->sctx, ilink->y, y, ADD_VALUES, SCATTER_REVERSE));
1391   } else if (jac->type == PC_COMPOSITE_MULTIPLICATIVE || jac->type == PC_COMPOSITE_SYMMETRIC_MULTIPLICATIVE) {
1392     if (!jac->w1) {
1393       PetscCall(VecDuplicate(x, &jac->w1));
1394       PetscCall(VecDuplicate(x, &jac->w2));
1395     }
1396     PetscCall(VecSet(y, 0.0));
1397     PetscCall(FieldSplitSplitSolveAdd(ilink, x, y));
1398     cnt = 1;
1399     while (ilink->next) {
1400       ilink = ilink->next;
1401       /* compute the residual only over the part of the vector needed */
1402       PetscCall(MatMult(jac->Afield[cnt++], y, ilink->x));
1403       PetscCall(VecScale(ilink->x, -1.0));
1404       PetscCall(VecScatterBegin(ilink->sctx, x, ilink->x, ADD_VALUES, SCATTER_FORWARD));
1405       PetscCall(VecScatterEnd(ilink->sctx, x, ilink->x, ADD_VALUES, SCATTER_FORWARD));
1406       PetscCall(PetscLogEventBegin(ilink->event, ilink->ksp, ilink->x, ilink->y, NULL));
1407       PetscCall(KSPSolve(ilink->ksp, ilink->x, ilink->y));
1408       PetscCall(KSPCheckSolve(ilink->ksp, pc, ilink->y));
1409       PetscCall(PetscLogEventEnd(ilink->event, ilink->ksp, ilink->x, ilink->y, NULL));
1410       PetscCall(VecScatterBegin(ilink->sctx, ilink->y, y, ADD_VALUES, SCATTER_REVERSE));
1411       PetscCall(VecScatterEnd(ilink->sctx, ilink->y, y, ADD_VALUES, SCATTER_REVERSE));
1412     }
1413     if (jac->type == PC_COMPOSITE_SYMMETRIC_MULTIPLICATIVE) {
1414       cnt -= 2;
1415       while (ilink->previous) {
1416         ilink = ilink->previous;
1417         /* compute the residual only over the part of the vector needed */
1418         PetscCall(MatMult(jac->Afield[cnt--], y, ilink->x));
1419         PetscCall(VecScale(ilink->x, -1.0));
1420         PetscCall(VecScatterBegin(ilink->sctx, x, ilink->x, ADD_VALUES, SCATTER_FORWARD));
1421         PetscCall(VecScatterEnd(ilink->sctx, x, ilink->x, ADD_VALUES, SCATTER_FORWARD));
1422         PetscCall(PetscLogEventBegin(ilink->event, ilink->ksp, ilink->x, ilink->y, NULL));
1423         PetscCall(KSPSolve(ilink->ksp, ilink->x, ilink->y));
1424         PetscCall(KSPCheckSolve(ilink->ksp, pc, ilink->y));
1425         PetscCall(PetscLogEventEnd(ilink->event, ilink->ksp, ilink->x, ilink->y, NULL));
1426         PetscCall(VecScatterBegin(ilink->sctx, ilink->y, y, ADD_VALUES, SCATTER_REVERSE));
1427         PetscCall(VecScatterEnd(ilink->sctx, ilink->y, y, ADD_VALUES, SCATTER_REVERSE));
1428       }
1429     }
1430   } else SETERRQ(PetscObjectComm((PetscObject)pc), PETSC_ERR_SUP, "Unsupported or unknown composition %d", (int)jac->type);
1431   PetscFunctionReturn(PETSC_SUCCESS);
1432 }
1433 
1434 static PetscErrorCode PCApply_FieldSplit_GKB(PC pc, Vec x, Vec y)
1435 {
1436   PC_FieldSplit    *jac    = (PC_FieldSplit *)pc->data;
1437   PC_FieldSplitLink ilinkA = jac->head, ilinkD = ilinkA->next;
1438   KSP               ksp = ilinkA->ksp;
1439   Vec               u, v, Hu, d, work1, work2;
1440   PetscScalar       alpha, z, nrmz2, *vecz;
1441   PetscReal         lowbnd, nu, beta;
1442   PetscInt          j, iterGKB;
1443 
1444   PetscFunctionBegin;
1445   PetscCall(VecScatterBegin(ilinkA->sctx, x, ilinkA->x, INSERT_VALUES, SCATTER_FORWARD));
1446   PetscCall(VecScatterBegin(ilinkD->sctx, x, ilinkD->x, INSERT_VALUES, SCATTER_FORWARD));
1447   PetscCall(VecScatterEnd(ilinkA->sctx, x, ilinkA->x, INSERT_VALUES, SCATTER_FORWARD));
1448   PetscCall(VecScatterEnd(ilinkD->sctx, x, ilinkD->x, INSERT_VALUES, SCATTER_FORWARD));
1449 
1450   u     = jac->u;
1451   v     = jac->v;
1452   Hu    = jac->Hu;
1453   d     = jac->d;
1454   work1 = jac->w1;
1455   work2 = jac->w2;
1456   vecz  = jac->vecz;
1457 
1458   /* Change RHS to comply with matrix regularization H = A + nu*B*B' */
1459   /* Add q = q + nu*B*b */
1460   if (jac->gkbnu) {
1461     nu = jac->gkbnu;
1462     PetscCall(VecScale(ilinkD->x, jac->gkbnu));
1463     PetscCall(MatMultAdd(jac->B, ilinkD->x, ilinkA->x, ilinkA->x)); /* q = q + nu*B*b */
1464   } else {
1465     /* Situation when no augmented Lagrangian is used. Then we set inner  */
1466     /* matrix N = I in [Ar13], and thus nu = 1.                           */
1467     nu = 1;
1468   }
1469 
1470   /* Transform rhs from [q,tilde{b}] to [0,b] */
1471   PetscCall(PetscLogEventBegin(ilinkA->event, ksp, ilinkA->x, ilinkA->y, NULL));
1472   PetscCall(KSPSolve(ksp, ilinkA->x, ilinkA->y));
1473   PetscCall(KSPCheckSolve(ksp, pc, ilinkA->y));
1474   PetscCall(PetscLogEventEnd(ilinkA->event, ksp, ilinkA->x, ilinkA->y, NULL));
1475   PetscCall(MatMultHermitianTranspose(jac->B, ilinkA->y, work1));
1476   PetscCall(VecAXPBY(work1, 1.0 / nu, -1.0, ilinkD->x)); /* c = b - B'*x        */
1477 
1478   /* First step of algorithm */
1479   PetscCall(VecNorm(work1, NORM_2, &beta)); /* beta = sqrt(nu*c'*c)*/
1480   KSPCheckDot(ksp, beta);
1481   beta = PetscSqrtReal(nu) * beta;
1482   PetscCall(VecAXPBY(v, nu / beta, 0.0, work1)); /* v = nu/beta *c      */
1483   PetscCall(MatMult(jac->B, v, work2));          /* u = H^{-1}*B*v      */
1484   PetscCall(PetscLogEventBegin(ilinkA->event, ksp, work2, u, NULL));
1485   PetscCall(KSPSolve(ksp, work2, u));
1486   PetscCall(KSPCheckSolve(ksp, pc, u));
1487   PetscCall(PetscLogEventEnd(ilinkA->event, ksp, work2, u, NULL));
1488   PetscCall(MatMult(jac->H, u, Hu)); /* alpha = u'*H*u      */
1489   PetscCall(VecDot(Hu, u, &alpha));
1490   KSPCheckDot(ksp, alpha);
1491   PetscCheck(PetscRealPart(alpha) > 0.0, PETSC_COMM_SELF, PETSC_ERR_NOT_CONVERGED, "GKB preconditioner diverged, H is not positive definite");
1492   alpha = PetscSqrtReal(PetscAbsScalar(alpha));
1493   PetscCall(VecScale(u, 1.0 / alpha));
1494   PetscCall(VecAXPBY(d, 1.0 / alpha, 0.0, v)); /* v = nu/beta *c      */
1495 
1496   z       = beta / alpha;
1497   vecz[1] = z;
1498 
1499   /* Computation of first iterate x(1) and p(1) */
1500   PetscCall(VecAXPY(ilinkA->y, z, u));
1501   PetscCall(VecCopy(d, ilinkD->y));
1502   PetscCall(VecScale(ilinkD->y, -z));
1503 
1504   iterGKB = 1;
1505   lowbnd  = 2 * jac->gkbtol;
1506   if (jac->gkbmonitor) PetscCall(PetscViewerASCIIPrintf(jac->gkbviewer, "%3" PetscInt_FMT " GKB Lower bound estimate %14.12e\n", iterGKB, (double)lowbnd));
1507 
1508   while (iterGKB < jac->gkbmaxit && lowbnd > jac->gkbtol) {
1509     iterGKB += 1;
1510     PetscCall(MatMultHermitianTranspose(jac->B, u, work1)); /* v <- nu*(B'*u-alpha/nu*v) */
1511     PetscCall(VecAXPBY(v, nu, -alpha, work1));
1512     PetscCall(VecNorm(v, NORM_2, &beta)); /* beta = sqrt(nu)*v'*v      */
1513     beta = beta / PetscSqrtReal(nu);
1514     PetscCall(VecScale(v, 1.0 / beta));
1515     PetscCall(MatMult(jac->B, v, work2)); /* u <- H^{-1}*(B*v-beta*H*u) */
1516     PetscCall(MatMult(jac->H, u, Hu));
1517     PetscCall(VecAXPY(work2, -beta, Hu));
1518     PetscCall(PetscLogEventBegin(ilinkA->event, ksp, work2, u, NULL));
1519     PetscCall(KSPSolve(ksp, work2, u));
1520     PetscCall(KSPCheckSolve(ksp, pc, u));
1521     PetscCall(PetscLogEventEnd(ilinkA->event, ksp, work2, u, NULL));
1522     PetscCall(MatMult(jac->H, u, Hu)); /* alpha = u'*H*u            */
1523     PetscCall(VecDot(Hu, u, &alpha));
1524     KSPCheckDot(ksp, alpha);
1525     PetscCheck(PetscRealPart(alpha) > 0.0, PETSC_COMM_SELF, PETSC_ERR_NOT_CONVERGED, "GKB preconditioner diverged, H is not positive definite");
1526     alpha = PetscSqrtReal(PetscAbsScalar(alpha));
1527     PetscCall(VecScale(u, 1.0 / alpha));
1528 
1529     z       = -beta / alpha * z; /* z <- beta/alpha*z     */
1530     vecz[0] = z;
1531 
1532     /* Computation of new iterate x(i+1) and p(i+1) */
1533     PetscCall(VecAXPBY(d, 1.0 / alpha, -beta / alpha, v)); /* d = (v-beta*d)/alpha */
1534     PetscCall(VecAXPY(ilinkA->y, z, u));                   /* r = r + z*u          */
1535     PetscCall(VecAXPY(ilinkD->y, -z, d));                  /* p = p - z*d          */
1536     PetscCall(MatMult(jac->H, ilinkA->y, Hu));             /* ||u||_H = u'*H*u     */
1537     PetscCall(VecDot(Hu, ilinkA->y, &nrmz2));
1538 
1539     /* Compute Lower Bound estimate */
1540     if (iterGKB > jac->gkbdelay) {
1541       lowbnd = 0.0;
1542       for (j = 0; j < jac->gkbdelay; j++) lowbnd += PetscAbsScalar(vecz[j] * vecz[j]);
1543       lowbnd = PetscSqrtReal(lowbnd / PetscAbsScalar(nrmz2));
1544     }
1545 
1546     for (j = 0; j < jac->gkbdelay - 1; j++) vecz[jac->gkbdelay - j - 1] = vecz[jac->gkbdelay - j - 2];
1547     if (jac->gkbmonitor) PetscCall(PetscViewerASCIIPrintf(jac->gkbviewer, "%3" PetscInt_FMT " GKB Lower bound estimate %14.12e\n", iterGKB, (double)lowbnd));
1548   }
1549 
1550   PetscCall(VecScatterBegin(ilinkA->sctx, ilinkA->y, y, INSERT_VALUES, SCATTER_REVERSE));
1551   PetscCall(VecScatterEnd(ilinkA->sctx, ilinkA->y, y, INSERT_VALUES, SCATTER_REVERSE));
1552   PetscCall(VecScatterBegin(ilinkD->sctx, ilinkD->y, y, INSERT_VALUES, SCATTER_REVERSE));
1553   PetscCall(VecScatterEnd(ilinkD->sctx, ilinkD->y, y, INSERT_VALUES, SCATTER_REVERSE));
1554 
1555   PetscFunctionReturn(PETSC_SUCCESS);
1556 }
1557 
1558 #define FieldSplitSplitSolveAddTranspose(ilink, xx, yy) \
1559   ((PetscErrorCode)(VecScatterBegin(ilink->sctx, xx, ilink->y, INSERT_VALUES, SCATTER_FORWARD) || VecScatterEnd(ilink->sctx, xx, ilink->y, INSERT_VALUES, SCATTER_FORWARD) || PetscLogEventBegin(ilink->event, ilink->ksp, ilink->y, ilink->x, NULL) || \
1560                     KSPSolveTranspose(ilink->ksp, ilink->y, ilink->x) || KSPCheckSolve(ilink->ksp, pc, ilink->x) || PetscLogEventEnd(ilink->event, ilink->ksp, ilink->y, ilink->x, NULL) || VecScatterBegin(ilink->sctx, ilink->x, yy, ADD_VALUES, SCATTER_REVERSE) || \
1561                     VecScatterEnd(ilink->sctx, ilink->x, yy, ADD_VALUES, SCATTER_REVERSE)))
1562 
1563 static PetscErrorCode PCApplyTranspose_FieldSplit(PC pc, Vec x, Vec y)
1564 {
1565   PC_FieldSplit    *jac   = (PC_FieldSplit *)pc->data;
1566   PC_FieldSplitLink ilink = jac->head;
1567   PetscInt          bs;
1568 
1569   PetscFunctionBegin;
1570   if (jac->type == PC_COMPOSITE_ADDITIVE) {
1571     if (jac->defaultsplit) {
1572       PetscCall(VecGetBlockSize(x, &bs));
1573       PetscCheck(jac->bs <= 0 || bs == jac->bs, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_WRONGSTATE, "Blocksize of x vector %" PetscInt_FMT " does not match fieldsplit blocksize %" PetscInt_FMT, bs, jac->bs);
1574       PetscCall(VecGetBlockSize(y, &bs));
1575       PetscCheck(jac->bs <= 0 || bs == jac->bs, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_WRONGSTATE, "Blocksize of y vector %" PetscInt_FMT " does not match fieldsplit blocksize %" PetscInt_FMT, bs, jac->bs);
1576       PetscCall(VecStrideGatherAll(x, jac->x, INSERT_VALUES));
1577       while (ilink) {
1578         PetscCall(PetscLogEventBegin(ilink->event, ilink->ksp, ilink->x, ilink->y, NULL));
1579         PetscCall(KSPSolveTranspose(ilink->ksp, ilink->x, ilink->y));
1580         PetscCall(KSPCheckSolve(ilink->ksp, pc, ilink->y));
1581         PetscCall(PetscLogEventEnd(ilink->event, ilink->ksp, ilink->x, ilink->y, NULL));
1582         ilink = ilink->next;
1583       }
1584       PetscCall(VecStrideScatterAll(jac->y, y, INSERT_VALUES));
1585     } else {
1586       PetscCall(VecSet(y, 0.0));
1587       while (ilink) {
1588         PetscCall(FieldSplitSplitSolveAddTranspose(ilink, x, y));
1589         ilink = ilink->next;
1590       }
1591     }
1592   } else {
1593     if (!jac->w1) {
1594       PetscCall(VecDuplicate(x, &jac->w1));
1595       PetscCall(VecDuplicate(x, &jac->w2));
1596     }
1597     PetscCall(VecSet(y, 0.0));
1598     if (jac->type == PC_COMPOSITE_SYMMETRIC_MULTIPLICATIVE) {
1599       PetscCall(FieldSplitSplitSolveAddTranspose(ilink, x, y));
1600       while (ilink->next) {
1601         ilink = ilink->next;
1602         PetscCall(MatMultTranspose(pc->mat, y, jac->w1));
1603         PetscCall(VecWAXPY(jac->w2, -1.0, jac->w1, x));
1604         PetscCall(FieldSplitSplitSolveAddTranspose(ilink, jac->w2, y));
1605       }
1606       while (ilink->previous) {
1607         ilink = ilink->previous;
1608         PetscCall(MatMultTranspose(pc->mat, y, jac->w1));
1609         PetscCall(VecWAXPY(jac->w2, -1.0, jac->w1, x));
1610         PetscCall(FieldSplitSplitSolveAddTranspose(ilink, jac->w2, y));
1611       }
1612     } else {
1613       while (ilink->next) { /* get to last entry in linked list */
1614         ilink = ilink->next;
1615       }
1616       PetscCall(FieldSplitSplitSolveAddTranspose(ilink, x, y));
1617       while (ilink->previous) {
1618         ilink = ilink->previous;
1619         PetscCall(MatMultTranspose(pc->mat, y, jac->w1));
1620         PetscCall(VecWAXPY(jac->w2, -1.0, jac->w1, x));
1621         PetscCall(FieldSplitSplitSolveAddTranspose(ilink, jac->w2, y));
1622       }
1623     }
1624   }
1625   PetscFunctionReturn(PETSC_SUCCESS);
1626 }
1627 
1628 static PetscErrorCode PCReset_FieldSplit(PC pc)
1629 {
1630   PC_FieldSplit    *jac   = (PC_FieldSplit *)pc->data;
1631   PC_FieldSplitLink ilink = jac->head, next;
1632 
1633   PetscFunctionBegin;
1634   while (ilink) {
1635     PetscCall(KSPDestroy(&ilink->ksp));
1636     PetscCall(VecDestroy(&ilink->x));
1637     PetscCall(VecDestroy(&ilink->y));
1638     PetscCall(VecDestroy(&ilink->z));
1639     PetscCall(VecScatterDestroy(&ilink->sctx));
1640     PetscCall(ISDestroy(&ilink->is));
1641     PetscCall(ISDestroy(&ilink->is_col));
1642     PetscCall(PetscFree(ilink->splitname));
1643     PetscCall(PetscFree(ilink->fields));
1644     PetscCall(PetscFree(ilink->fields_col));
1645     next = ilink->next;
1646     PetscCall(PetscFree(ilink));
1647     ilink = next;
1648   }
1649   jac->head = NULL;
1650   PetscCall(PetscFree2(jac->x, jac->y));
1651   if (jac->mat && jac->mat != jac->pmat) {
1652     PetscCall(MatDestroyMatrices(jac->nsplits, &jac->mat));
1653   } else if (jac->mat) {
1654     jac->mat = NULL;
1655   }
1656   if (jac->pmat) PetscCall(MatDestroyMatrices(jac->nsplits, &jac->pmat));
1657   if (jac->Afield) PetscCall(MatDestroyMatrices(jac->nsplits, &jac->Afield));
1658   jac->nsplits = 0;
1659   PetscCall(VecDestroy(&jac->w1));
1660   PetscCall(VecDestroy(&jac->w2));
1661   PetscCall(MatDestroy(&jac->schur));
1662   PetscCall(MatDestroy(&jac->schurp));
1663   PetscCall(MatDestroy(&jac->schur_user));
1664   PetscCall(KSPDestroy(&jac->kspschur));
1665   PetscCall(KSPDestroy(&jac->kspupper));
1666   PetscCall(MatDestroy(&jac->B));
1667   PetscCall(MatDestroy(&jac->C));
1668   PetscCall(MatDestroy(&jac->H));
1669   PetscCall(VecDestroy(&jac->u));
1670   PetscCall(VecDestroy(&jac->v));
1671   PetscCall(VecDestroy(&jac->Hu));
1672   PetscCall(VecDestroy(&jac->d));
1673   PetscCall(PetscFree(jac->vecz));
1674   PetscCall(PetscViewerDestroy(&jac->gkbviewer));
1675   jac->isrestrict = PETSC_FALSE;
1676   PetscFunctionReturn(PETSC_SUCCESS);
1677 }
1678 
1679 static PetscErrorCode PCDestroy_FieldSplit(PC pc)
1680 {
1681   PetscFunctionBegin;
1682   PetscCall(PCReset_FieldSplit(pc));
1683   PetscCall(PetscFree(pc->data));
1684   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCSetCoordinates_C", NULL));
1685   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetFields_C", NULL));
1686   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetIS_C", NULL));
1687   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetType_C", NULL));
1688   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetBlockSize_C", NULL));
1689   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitRestrictIS_C", NULL));
1690   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSchurGetSubKSP_C", NULL));
1691   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitGetSubKSP_C", NULL));
1692 
1693   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetGKBTol_C", NULL));
1694   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetGKBMaxit_C", NULL));
1695   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetGKBNu_C", NULL));
1696   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetGKBDelay_C", NULL));
1697   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitGetSubKSP_C", NULL));
1698   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetSchurPre_C", NULL));
1699   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitGetSchurPre_C", NULL));
1700   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetSchurFactType_C", NULL));
1701   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetSchurScale_C", NULL));
1702   PetscFunctionReturn(PETSC_SUCCESS);
1703 }
1704 
1705 static PetscErrorCode PCSetFromOptions_FieldSplit(PC pc, PetscOptionItems *PetscOptionsObject)
1706 {
1707   PetscInt        bs;
1708   PetscBool       flg;
1709   PC_FieldSplit  *jac = (PC_FieldSplit *)pc->data;
1710   PCCompositeType ctype;
1711 
1712   PetscFunctionBegin;
1713   PetscOptionsHeadBegin(PetscOptionsObject, "FieldSplit options");
1714   PetscCall(PetscOptionsBool("-pc_fieldsplit_dm_splits", "Whether to use DMCreateFieldDecomposition() for splits", "PCFieldSplitSetDMSplits", jac->dm_splits, &jac->dm_splits, NULL));
1715   PetscCall(PetscOptionsInt("-pc_fieldsplit_block_size", "Blocksize that defines number of fields", "PCFieldSplitSetBlockSize", jac->bs, &bs, &flg));
1716   if (flg) PetscCall(PCFieldSplitSetBlockSize(pc, bs));
1717   jac->diag_use_amat = pc->useAmat;
1718   PetscCall(PetscOptionsBool("-pc_fieldsplit_diag_use_amat", "Use Amat (not Pmat) to extract diagonal fieldsplit blocks", "PCFieldSplitSetDiagUseAmat", jac->diag_use_amat, &jac->diag_use_amat, NULL));
1719   jac->offdiag_use_amat = pc->useAmat;
1720   PetscCall(PetscOptionsBool("-pc_fieldsplit_off_diag_use_amat", "Use Amat (not Pmat) to extract off-diagonal fieldsplit blocks", "PCFieldSplitSetOffDiagUseAmat", jac->offdiag_use_amat, &jac->offdiag_use_amat, NULL));
1721   PetscCall(PetscOptionsBool("-pc_fieldsplit_detect_saddle_point", "Form 2-way split by detecting zero diagonal entries", "PCFieldSplitSetDetectSaddlePoint", jac->detect, &jac->detect, NULL));
1722   PetscCall(PCFieldSplitSetDetectSaddlePoint(pc, jac->detect)); /* Sets split type and Schur PC type */
1723   PetscCall(PetscOptionsEnum("-pc_fieldsplit_type", "Type of composition", "PCFieldSplitSetType", PCCompositeTypes, (PetscEnum)jac->type, (PetscEnum *)&ctype, &flg));
1724   if (flg) PetscCall(PCFieldSplitSetType(pc, ctype));
1725   /* Only setup fields once */
1726   if ((jac->bs > 0) && (jac->nsplits == 0)) {
1727     /* only allow user to set fields from command line if bs is already known.
1728        otherwise user can set them in PCFieldSplitSetDefaults() */
1729     PetscCall(PCFieldSplitSetRuntimeSplits_Private(pc));
1730     if (jac->splitdefined) PetscCall(PetscInfo(pc, "Splits defined using the options database\n"));
1731   }
1732   if (jac->type == PC_COMPOSITE_SCHUR) {
1733     PetscCall(PetscOptionsGetEnum(((PetscObject)pc)->options, ((PetscObject)pc)->prefix, "-pc_fieldsplit_schur_factorization_type", PCFieldSplitSchurFactTypes, (PetscEnum *)&jac->schurfactorization, &flg));
1734     if (flg) PetscCall(PetscInfo(pc, "Deprecated use of -pc_fieldsplit_schur_factorization_type\n"));
1735     PetscCall(PetscOptionsEnum("-pc_fieldsplit_schur_fact_type", "Which off-diagonal parts of the block factorization to use", "PCFieldSplitSetSchurFactType", PCFieldSplitSchurFactTypes, (PetscEnum)jac->schurfactorization, (PetscEnum *)&jac->schurfactorization, NULL));
1736     PetscCall(PetscOptionsEnum("-pc_fieldsplit_schur_precondition", "How to build preconditioner for Schur complement", "PCFieldSplitSetSchurPre", PCFieldSplitSchurPreTypes, (PetscEnum)jac->schurpre, (PetscEnum *)&jac->schurpre, NULL));
1737     PetscCall(PetscOptionsScalar("-pc_fieldsplit_schur_scale", "Scale Schur complement", "PCFieldSplitSetSchurScale", jac->schurscale, &jac->schurscale, NULL));
1738   } else if (jac->type == PC_COMPOSITE_GKB) {
1739     PetscCall(PetscOptionsReal("-pc_fieldsplit_gkb_tol", "The tolerance for the lower bound stopping criterion", "PCFieldSplitGKBTol", jac->gkbtol, &jac->gkbtol, NULL));
1740     PetscCall(PetscOptionsInt("-pc_fieldsplit_gkb_delay", "The delay value for lower bound criterion", "PCFieldSplitGKBDelay", jac->gkbdelay, &jac->gkbdelay, NULL));
1741     PetscCall(PetscOptionsReal("-pc_fieldsplit_gkb_nu", "Parameter in augmented Lagrangian approach", "PCFieldSplitGKBNu", jac->gkbnu, &jac->gkbnu, NULL));
1742     PetscCheck(jac->gkbnu >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "nu cannot be less than 0: value %g", (double)jac->gkbnu);
1743     PetscCall(PetscOptionsInt("-pc_fieldsplit_gkb_maxit", "Maximum allowed number of iterations", "PCFieldSplitGKBMaxit", jac->gkbmaxit, &jac->gkbmaxit, NULL));
1744     PetscCall(PetscOptionsBool("-pc_fieldsplit_gkb_monitor", "Prints number of GKB iterations and error", "PCFieldSplitGKB", jac->gkbmonitor, &jac->gkbmonitor, NULL));
1745   }
1746   /*
1747     In the initial call to this routine the sub-solver data structures do not exist so we cannot call KSPSetFromOptions() on them yet.
1748     But after the initial setup of ALL the layers of sub-solvers is completed we do want to call KSPSetFromOptions() on the sub-solvers every time it
1749     is called on the outer solver in case changes were made in the options database
1750 
1751     But even after PCSetUp_FieldSplit() is called all the options inside the inner levels of sub-solvers may still not have been set thus we only call the KSPSetFromOptions()
1752     if we know that the entire stack of sub-solvers below this have been complete instantiated, we check this by seeing if any solver iterations are complete.
1753     Without this extra check test p2p1fetidp_olof_full and others fail with incorrect matrix types.
1754 
1755     There could be a negative side effect of calling the KSPSetFromOptions() below.
1756 
1757     If one captured the PetscObjectState of the options database one could skip these calls if the database has not changed from the previous call
1758   */
1759   if (jac->issetup) {
1760     PC_FieldSplitLink ilink = jac->head;
1761     if (jac->type == PC_COMPOSITE_SCHUR) {
1762       if (jac->kspupper && jac->kspupper->totalits > 0) PetscCall(KSPSetFromOptions(jac->kspupper));
1763       if (jac->kspschur && jac->kspschur->totalits > 0) PetscCall(KSPSetFromOptions(jac->kspschur));
1764     }
1765     while (ilink) {
1766       if (ilink->ksp->totalits > 0) PetscCall(KSPSetFromOptions(ilink->ksp));
1767       ilink = ilink->next;
1768     }
1769   }
1770   PetscOptionsHeadEnd();
1771   PetscFunctionReturn(PETSC_SUCCESS);
1772 }
1773 
1774 static PetscErrorCode PCFieldSplitSetFields_FieldSplit(PC pc, const char splitname[], PetscInt n, const PetscInt *fields, const PetscInt *fields_col)
1775 {
1776   PC_FieldSplit    *jac = (PC_FieldSplit *)pc->data;
1777   PC_FieldSplitLink ilink, next = jac->head;
1778   char              prefix[128];
1779   PetscInt          i;
1780 
1781   PetscFunctionBegin;
1782   if (jac->splitdefined) {
1783     PetscCall(PetscInfo(pc, "Ignoring new split \"%s\" because the splits have already been defined\n", splitname));
1784     PetscFunctionReturn(PETSC_SUCCESS);
1785   }
1786   for (i = 0; i < n; i++) {
1787     PetscCheck(fields[i] < jac->bs, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Field %" PetscInt_FMT " requested but only %" PetscInt_FMT " exist", fields[i], jac->bs);
1788     PetscCheck(fields[i] >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Negative field %" PetscInt_FMT " requested", fields[i]);
1789   }
1790   PetscCall(PetscNew(&ilink));
1791   if (splitname) {
1792     PetscCall(PetscStrallocpy(splitname, &ilink->splitname));
1793   } else {
1794     PetscCall(PetscMalloc1(3, &ilink->splitname));
1795     PetscCall(PetscSNPrintf(ilink->splitname, 2, "%" PetscInt_FMT, jac->nsplits));
1796   }
1797   ilink->event = jac->nsplits < 5 ? KSP_Solve_FS_0 + jac->nsplits : KSP_Solve_FS_0 + 4; /* Any split great than 4 gets logged in the 4th split */
1798   PetscCall(PetscMalloc1(n, &ilink->fields));
1799   PetscCall(PetscArraycpy(ilink->fields, fields, n));
1800   PetscCall(PetscMalloc1(n, &ilink->fields_col));
1801   PetscCall(PetscArraycpy(ilink->fields_col, fields_col, n));
1802 
1803   ilink->nfields = n;
1804   ilink->next    = NULL;
1805   PetscCall(KSPCreate(PetscObjectComm((PetscObject)pc), &ilink->ksp));
1806   PetscCall(KSPSetNestLevel(ilink->ksp, pc->kspnestlevel));
1807   PetscCall(KSPSetErrorIfNotConverged(ilink->ksp, pc->erroriffailure));
1808   PetscCall(PetscObjectIncrementTabLevel((PetscObject)ilink->ksp, (PetscObject)pc, 1));
1809   PetscCall(KSPSetType(ilink->ksp, KSPPREONLY));
1810 
1811   PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "%sfieldsplit_%s_", ((PetscObject)pc)->prefix ? ((PetscObject)pc)->prefix : "", ilink->splitname));
1812   PetscCall(KSPSetOptionsPrefix(ilink->ksp, prefix));
1813 
1814   if (!next) {
1815     jac->head       = ilink;
1816     ilink->previous = NULL;
1817   } else {
1818     while (next->next) next = next->next;
1819     next->next      = ilink;
1820     ilink->previous = next;
1821   }
1822   jac->nsplits++;
1823   PetscFunctionReturn(PETSC_SUCCESS);
1824 }
1825 
1826 static PetscErrorCode PCFieldSplitSchurGetSubKSP_FieldSplit(PC pc, PetscInt *n, KSP **subksp)
1827 {
1828   PC_FieldSplit *jac = (PC_FieldSplit *)pc->data;
1829 
1830   PetscFunctionBegin;
1831   *subksp = NULL;
1832   if (n) *n = 0;
1833   if (jac->type == PC_COMPOSITE_SCHUR) {
1834     PetscInt nn;
1835 
1836     PetscCheck(jac->schur, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_WRONGSTATE, "Must call KSPSetUp() or PCSetUp() before calling PCFieldSplitSchurGetSubKSP()");
1837     PetscCheck(jac->nsplits == 2, PetscObjectComm((PetscObject)pc), PETSC_ERR_PLIB, "Unexpected number of splits %" PetscInt_FMT " != 2", jac->nsplits);
1838     nn = jac->nsplits + (jac->kspupper != jac->head->ksp ? 1 : 0);
1839     PetscCall(PetscMalloc1(nn, subksp));
1840     (*subksp)[0] = jac->head->ksp;
1841     (*subksp)[1] = jac->kspschur;
1842     if (jac->kspupper != jac->head->ksp) (*subksp)[2] = jac->kspupper;
1843     if (n) *n = nn;
1844   }
1845   PetscFunctionReturn(PETSC_SUCCESS);
1846 }
1847 
1848 static PetscErrorCode PCFieldSplitGetSubKSP_FieldSplit_Schur(PC pc, PetscInt *n, KSP **subksp)
1849 {
1850   PC_FieldSplit *jac = (PC_FieldSplit *)pc->data;
1851 
1852   PetscFunctionBegin;
1853   PetscCheck(jac->schur, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_WRONGSTATE, "Must call KSPSetUp() or PCSetUp() before calling PCFieldSplitGetSubKSP()");
1854   PetscCall(PetscMalloc1(jac->nsplits, subksp));
1855   PetscCall(MatSchurComplementGetKSP(jac->schur, *subksp));
1856 
1857   (*subksp)[1] = jac->kspschur;
1858   if (n) *n = jac->nsplits;
1859   PetscFunctionReturn(PETSC_SUCCESS);
1860 }
1861 
1862 static PetscErrorCode PCFieldSplitGetSubKSP_FieldSplit(PC pc, PetscInt *n, KSP **subksp)
1863 {
1864   PC_FieldSplit    *jac   = (PC_FieldSplit *)pc->data;
1865   PetscInt          cnt   = 0;
1866   PC_FieldSplitLink ilink = jac->head;
1867 
1868   PetscFunctionBegin;
1869   PetscCall(PetscMalloc1(jac->nsplits, subksp));
1870   while (ilink) {
1871     (*subksp)[cnt++] = ilink->ksp;
1872     ilink            = ilink->next;
1873   }
1874   PetscCheck(cnt == jac->nsplits, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Corrupt PCFIELDSPLIT object: number of splits in linked list %" PetscInt_FMT " does not match number in object %" PetscInt_FMT, cnt, jac->nsplits);
1875   if (n) *n = jac->nsplits;
1876   PetscFunctionReturn(PETSC_SUCCESS);
1877 }
1878 
1879 /*@C
1880   PCFieldSplitRestrictIS - Restricts the fieldsplit `IS`s to be within a given `IS`.
1881 
1882   Input Parameters:
1883 + pc  - the preconditioner context
1884 - isy - the index set that defines the indices to which the fieldsplit is to be restricted
1885 
1886   Level: advanced
1887 
1888   Developer Notes:
1889   It seems the resulting `IS`s will not cover the entire space, so
1890   how can they define a convergent preconditioner? Needs explaining.
1891 
1892 .seealso: [](sec_block_matrices), `PCFIELDSPLIT`, `PCFieldSplitSetFields()`, `PCFieldSplitSetIS()`
1893 @*/
1894 PetscErrorCode PCFieldSplitRestrictIS(PC pc, IS isy)
1895 {
1896   PetscFunctionBegin;
1897   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
1898   PetscValidHeaderSpecific(isy, IS_CLASSID, 2);
1899   PetscTryMethod(pc, "PCFieldSplitRestrictIS_C", (PC, IS), (pc, isy));
1900   PetscFunctionReturn(PETSC_SUCCESS);
1901 }
1902 
1903 static PetscErrorCode PCFieldSplitRestrictIS_FieldSplit(PC pc, IS isy)
1904 {
1905   PC_FieldSplit    *jac   = (PC_FieldSplit *)pc->data;
1906   PC_FieldSplitLink ilink = jac->head, next;
1907   PetscInt          localsize, size, sizez, i;
1908   const PetscInt   *ind, *indz;
1909   PetscInt         *indc, *indcz;
1910   PetscBool         flg;
1911 
1912   PetscFunctionBegin;
1913   PetscCall(ISGetLocalSize(isy, &localsize));
1914   PetscCallMPI(MPI_Scan(&localsize, &size, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)isy)));
1915   size -= localsize;
1916   while (ilink) {
1917     IS isrl, isr;
1918     PC subpc;
1919     PetscCall(ISEmbed(ilink->is, isy, PETSC_TRUE, &isrl));
1920     PetscCall(ISGetLocalSize(isrl, &localsize));
1921     PetscCall(PetscMalloc1(localsize, &indc));
1922     PetscCall(ISGetIndices(isrl, &ind));
1923     PetscCall(PetscArraycpy(indc, ind, localsize));
1924     PetscCall(ISRestoreIndices(isrl, &ind));
1925     PetscCall(ISDestroy(&isrl));
1926     for (i = 0; i < localsize; i++) *(indc + i) += size;
1927     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)isy), localsize, indc, PETSC_OWN_POINTER, &isr));
1928     PetscCall(PetscObjectReference((PetscObject)isr));
1929     PetscCall(ISDestroy(&ilink->is));
1930     ilink->is = isr;
1931     PetscCall(PetscObjectReference((PetscObject)isr));
1932     PetscCall(ISDestroy(&ilink->is_col));
1933     ilink->is_col = isr;
1934     PetscCall(ISDestroy(&isr));
1935     PetscCall(KSPGetPC(ilink->ksp, &subpc));
1936     PetscCall(PetscObjectTypeCompare((PetscObject)subpc, PCFIELDSPLIT, &flg));
1937     if (flg) {
1938       IS       iszl, isz;
1939       MPI_Comm comm;
1940       PetscCall(ISGetLocalSize(ilink->is, &localsize));
1941       comm = PetscObjectComm((PetscObject)ilink->is);
1942       PetscCall(ISEmbed(isy, ilink->is, PETSC_TRUE, &iszl));
1943       PetscCallMPI(MPI_Scan(&localsize, &sizez, 1, MPIU_INT, MPI_SUM, comm));
1944       sizez -= localsize;
1945       PetscCall(ISGetLocalSize(iszl, &localsize));
1946       PetscCall(PetscMalloc1(localsize, &indcz));
1947       PetscCall(ISGetIndices(iszl, &indz));
1948       PetscCall(PetscArraycpy(indcz, indz, localsize));
1949       PetscCall(ISRestoreIndices(iszl, &indz));
1950       PetscCall(ISDestroy(&iszl));
1951       for (i = 0; i < localsize; i++) *(indcz + i) += sizez;
1952       PetscCall(ISCreateGeneral(comm, localsize, indcz, PETSC_OWN_POINTER, &isz));
1953       PetscCall(PCFieldSplitRestrictIS(subpc, isz));
1954       PetscCall(ISDestroy(&isz));
1955     }
1956     next  = ilink->next;
1957     ilink = next;
1958   }
1959   jac->isrestrict = PETSC_TRUE;
1960   PetscFunctionReturn(PETSC_SUCCESS);
1961 }
1962 
1963 static PetscErrorCode PCFieldSplitSetIS_FieldSplit(PC pc, const char splitname[], IS is)
1964 {
1965   PC_FieldSplit    *jac = (PC_FieldSplit *)pc->data;
1966   PC_FieldSplitLink ilink, next = jac->head;
1967   char              prefix[128];
1968 
1969   PetscFunctionBegin;
1970   if (jac->splitdefined) {
1971     PetscCall(PetscInfo(pc, "Ignoring new split \"%s\" because the splits have already been defined\n", splitname));
1972     PetscFunctionReturn(PETSC_SUCCESS);
1973   }
1974   PetscCall(PetscNew(&ilink));
1975   if (splitname) {
1976     PetscCall(PetscStrallocpy(splitname, &ilink->splitname));
1977   } else {
1978     PetscCall(PetscMalloc1(8, &ilink->splitname));
1979     PetscCall(PetscSNPrintf(ilink->splitname, 7, "%" PetscInt_FMT, jac->nsplits));
1980   }
1981   ilink->event = jac->nsplits < 5 ? KSP_Solve_FS_0 + jac->nsplits : KSP_Solve_FS_0 + 4; /* Any split great than 4 gets logged in the 4th split */
1982   PetscCall(PetscObjectReference((PetscObject)is));
1983   PetscCall(ISDestroy(&ilink->is));
1984   ilink->is = is;
1985   PetscCall(PetscObjectReference((PetscObject)is));
1986   PetscCall(ISDestroy(&ilink->is_col));
1987   ilink->is_col = is;
1988   ilink->next   = NULL;
1989   PetscCall(KSPCreate(PetscObjectComm((PetscObject)pc), &ilink->ksp));
1990   PetscCall(KSPSetNestLevel(ilink->ksp, pc->kspnestlevel));
1991   PetscCall(KSPSetErrorIfNotConverged(ilink->ksp, pc->erroriffailure));
1992   PetscCall(PetscObjectIncrementTabLevel((PetscObject)ilink->ksp, (PetscObject)pc, 1));
1993   PetscCall(KSPSetType(ilink->ksp, KSPPREONLY));
1994 
1995   PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "%sfieldsplit_%s_", ((PetscObject)pc)->prefix ? ((PetscObject)pc)->prefix : "", ilink->splitname));
1996   PetscCall(KSPSetOptionsPrefix(ilink->ksp, prefix));
1997 
1998   if (!next) {
1999     jac->head       = ilink;
2000     ilink->previous = NULL;
2001   } else {
2002     while (next->next) next = next->next;
2003     next->next      = ilink;
2004     ilink->previous = next;
2005   }
2006   jac->nsplits++;
2007   PetscFunctionReturn(PETSC_SUCCESS);
2008 }
2009 
2010 /*@C
2011   PCFieldSplitSetFields - Sets the fields that define one particular split in `PCFIELDSPLIT`
2012 
2013   Logically Collective
2014 
2015   Input Parameters:
2016 + pc         - the preconditioner context
2017 . splitname  - name of this split, if `NULL` the number of the split is used
2018 . n          - the number of fields in this split
2019 . fields     - the fields in this split
2020 - fields_col - generally the same as fields, if it does not match fields then the matrix block that is solved for this set of fields comes from an off-diagonal block
2021                  of the matrix and fields_col provides the column indices for that block
2022 
2023   Level: intermediate
2024 
2025   Notes:
2026   Use `PCFieldSplitSetIS()` to set a  general set of indices as a split.
2027 
2028   `PCFieldSplitSetFields()` is for defining fields as strided blocks. For example, if the block
2029   size is three then one can define a split as 0, or 1 or 2 or 0,1 or 0,2 or 1,2 which mean
2030   0xx3xx6xx9xx12 ... x1xx4xx7xx ... xx2xx5xx8xx.. 01x34x67x... 0x1x3x5x7.. x12x45x78x....
2031   where the numbered entries indicate what is in the split.
2032 
2033   This function is called once per split (it creates a new split each time).  Solve options
2034   for this split will be available under the prefix `-fieldsplit_SPLITNAME_`.
2035 
2036   `PCFieldSplitSetIS()` does not support having a fields_col different from fields
2037 
2038   Developer Notes:
2039   This routine does not actually create the `IS` representing the split, that is delayed
2040   until `PCSetUp_FieldSplit()`, because information about the vector/matrix layouts may not be
2041   available when this routine is called.
2042 
2043 .seealso: [](sec_block_matrices), `PC`, `PCFieldSplitGetSubKSP()`, `PCFIELDSPLIT`, `PCFieldSplitSetBlockSize()`, `PCFieldSplitSetIS()`, `PCFieldSplitRestrictIS()`
2044 @*/
2045 PetscErrorCode PCFieldSplitSetFields(PC pc, const char splitname[], PetscInt n, const PetscInt *fields, const PetscInt *fields_col)
2046 {
2047   PetscFunctionBegin;
2048   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
2049   PetscAssertPointer(splitname, 2);
2050   PetscCheck(n >= 1, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_OUTOFRANGE, "Provided number of fields %" PetscInt_FMT " in split \"%s\" not positive", n, splitname);
2051   PetscAssertPointer(fields, 4);
2052   PetscTryMethod(pc, "PCFieldSplitSetFields_C", (PC, const char[], PetscInt, const PetscInt *, const PetscInt *), (pc, splitname, n, fields, fields_col));
2053   PetscFunctionReturn(PETSC_SUCCESS);
2054 }
2055 
2056 /*@
2057   PCFieldSplitSetDiagUseAmat - set flag indicating whether to extract diagonal blocks from Amat (rather than Pmat) to build
2058   the sub-matrices associated with each split. Where `KSPSetOperators`(ksp,Amat,Pmat)) was used to supply the operators.
2059 
2060   Logically Collective
2061 
2062   Input Parameters:
2063 + pc  - the preconditioner object
2064 - flg - boolean flag indicating whether or not to use Amat to extract the diagonal blocks from
2065 
2066   Options Database Key:
2067 . -pc_fieldsplit_diag_use_amat - use the Amat to provide the diagonal blocks
2068 
2069   Level: intermediate
2070 
2071 .seealso: [](sec_block_matrices), `PC`, `PCSetOperators()`, `KSPSetOperators()`, `PCFieldSplitGetDiagUseAmat()`, `PCFieldSplitSetOffDiagUseAmat()`, `PCFIELDSPLIT`
2072 @*/
2073 PetscErrorCode PCFieldSplitSetDiagUseAmat(PC pc, PetscBool flg)
2074 {
2075   PC_FieldSplit *jac = (PC_FieldSplit *)pc->data;
2076   PetscBool      isfs;
2077 
2078   PetscFunctionBegin;
2079   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
2080   PetscCall(PetscObjectTypeCompare((PetscObject)pc, PCFIELDSPLIT, &isfs));
2081   PetscCheck(isfs, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "PC not of type %s", PCFIELDSPLIT);
2082   jac->diag_use_amat = flg;
2083   PetscFunctionReturn(PETSC_SUCCESS);
2084 }
2085 
2086 /*@
2087   PCFieldSplitGetDiagUseAmat - get the flag indicating whether to extract diagonal blocks from Amat (rather than Pmat) to build
2088   the sub-matrices associated with each split.  Where `KSPSetOperators`(ksp,Amat,Pmat)) was used to supply the operators.
2089 
2090   Logically Collective
2091 
2092   Input Parameter:
2093 . pc - the preconditioner object
2094 
2095   Output Parameter:
2096 . flg - boolean flag indicating whether or not to use Amat to extract the diagonal blocks from
2097 
2098   Level: intermediate
2099 
2100 .seealso: [](sec_block_matrices), `PC`, `PCSetOperators()`, `KSPSetOperators()`, `PCFieldSplitSetDiagUseAmat()`, `PCFieldSplitGetOffDiagUseAmat()`, `PCFIELDSPLIT`
2101 @*/
2102 PetscErrorCode PCFieldSplitGetDiagUseAmat(PC pc, PetscBool *flg)
2103 {
2104   PC_FieldSplit *jac = (PC_FieldSplit *)pc->data;
2105   PetscBool      isfs;
2106 
2107   PetscFunctionBegin;
2108   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
2109   PetscAssertPointer(flg, 2);
2110   PetscCall(PetscObjectTypeCompare((PetscObject)pc, PCFIELDSPLIT, &isfs));
2111   PetscCheck(isfs, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "PC not of type %s", PCFIELDSPLIT);
2112   *flg = jac->diag_use_amat;
2113   PetscFunctionReturn(PETSC_SUCCESS);
2114 }
2115 
2116 /*@
2117   PCFieldSplitSetOffDiagUseAmat - set flag indicating whether to extract off-diagonal blocks from Amat (rather than Pmat) to build
2118   the sub-matrices associated with each split.  Where `KSPSetOperators`(ksp,Amat,Pmat)) was used to supply the operators.
2119 
2120   Logically Collective
2121 
2122   Input Parameters:
2123 + pc  - the preconditioner object
2124 - flg - boolean flag indicating whether or not to use Amat to extract the off-diagonal blocks from
2125 
2126   Options Database Key:
2127 . -pc_fieldsplit_off_diag_use_amat <bool> - use the Amat to extract the off-diagonal blocks
2128 
2129   Level: intermediate
2130 
2131 .seealso: [](sec_block_matrices), `PC`, `PCSetOperators()`, `KSPSetOperators()`, `PCFieldSplitGetOffDiagUseAmat()`, `PCFieldSplitSetDiagUseAmat()`, `PCFIELDSPLIT`
2132 @*/
2133 PetscErrorCode PCFieldSplitSetOffDiagUseAmat(PC pc, PetscBool flg)
2134 {
2135   PC_FieldSplit *jac = (PC_FieldSplit *)pc->data;
2136   PetscBool      isfs;
2137 
2138   PetscFunctionBegin;
2139   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
2140   PetscCall(PetscObjectTypeCompare((PetscObject)pc, PCFIELDSPLIT, &isfs));
2141   PetscCheck(isfs, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "PC not of type %s", PCFIELDSPLIT);
2142   jac->offdiag_use_amat = flg;
2143   PetscFunctionReturn(PETSC_SUCCESS);
2144 }
2145 
2146 /*@
2147   PCFieldSplitGetOffDiagUseAmat - get the flag indicating whether to extract off-diagonal blocks from Amat (rather than Pmat) to build
2148   the sub-matrices associated with each split.  Where `KSPSetOperators`(ksp,Amat,Pmat)) was used to supply the operators.
2149 
2150   Logically Collective
2151 
2152   Input Parameter:
2153 . pc - the preconditioner object
2154 
2155   Output Parameter:
2156 . flg - boolean flag indicating whether or not to use Amat to extract the off-diagonal blocks from
2157 
2158   Level: intermediate
2159 
2160 .seealso: [](sec_block_matrices), `PC`, `PCSetOperators()`, `KSPSetOperators()`, `PCFieldSplitSetOffDiagUseAmat()`, `PCFieldSplitGetDiagUseAmat()`, `PCFIELDSPLIT`
2161 @*/
2162 PetscErrorCode PCFieldSplitGetOffDiagUseAmat(PC pc, PetscBool *flg)
2163 {
2164   PC_FieldSplit *jac = (PC_FieldSplit *)pc->data;
2165   PetscBool      isfs;
2166 
2167   PetscFunctionBegin;
2168   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
2169   PetscAssertPointer(flg, 2);
2170   PetscCall(PetscObjectTypeCompare((PetscObject)pc, PCFIELDSPLIT, &isfs));
2171   PetscCheck(isfs, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "PC not of type %s", PCFIELDSPLIT);
2172   *flg = jac->offdiag_use_amat;
2173   PetscFunctionReturn(PETSC_SUCCESS);
2174 }
2175 
2176 /*@C
2177   PCFieldSplitSetIS - Sets the exact elements for a split in a `PCFIELDSPLIT`
2178 
2179   Logically Collective
2180 
2181   Input Parameters:
2182 + pc        - the preconditioner context
2183 . splitname - name of this split, if `NULL` the number of the split is used
2184 - is        - the index set that defines the elements in this split
2185 
2186   Level: intermediate
2187 
2188   Notes:
2189   Use `PCFieldSplitSetFields()`, for splits defined by strided types.
2190 
2191   This function is called once per split (it creates a new split each time).  Solve options
2192   for this split will be available under the prefix -fieldsplit_SPLITNAME_.
2193 
2194 .seealso: [](sec_block_matrices), `PC`, `PCFieldSplitGetSubKSP()`, `PCFIELDSPLIT`, `PCFieldSplitSetBlockSize()`
2195 @*/
2196 PetscErrorCode PCFieldSplitSetIS(PC pc, const char splitname[], IS is)
2197 {
2198   PetscFunctionBegin;
2199   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
2200   if (splitname) PetscAssertPointer(splitname, 2);
2201   PetscValidHeaderSpecific(is, IS_CLASSID, 3);
2202   PetscTryMethod(pc, "PCFieldSplitSetIS_C", (PC, const char[], IS), (pc, splitname, is));
2203   PetscFunctionReturn(PETSC_SUCCESS);
2204 }
2205 
2206 /*@C
2207   PCFieldSplitGetIS - Retrieves the elements for a split as an `IS`
2208 
2209   Logically Collective
2210 
2211   Input Parameters:
2212 + pc        - the preconditioner context
2213 - splitname - name of this split
2214 
2215   Output Parameter:
2216 . is - the index set that defines the elements in this split, or `NULL` if the split is not found
2217 
2218   Level: intermediate
2219 
2220 .seealso: [](sec_block_matrices), `PC`, `PCFieldSplitGetSubKSP()`, `PCFIELDSPLIT`, `PCFieldSplitSetIS()`
2221 @*/
2222 PetscErrorCode PCFieldSplitGetIS(PC pc, const char splitname[], IS *is)
2223 {
2224   PetscFunctionBegin;
2225   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
2226   PetscAssertPointer(splitname, 2);
2227   PetscAssertPointer(is, 3);
2228   {
2229     PC_FieldSplit    *jac   = (PC_FieldSplit *)pc->data;
2230     PC_FieldSplitLink ilink = jac->head;
2231     PetscBool         found;
2232 
2233     *is = NULL;
2234     while (ilink) {
2235       PetscCall(PetscStrcmp(ilink->splitname, splitname, &found));
2236       if (found) {
2237         *is = ilink->is;
2238         break;
2239       }
2240       ilink = ilink->next;
2241     }
2242   }
2243   PetscFunctionReturn(PETSC_SUCCESS);
2244 }
2245 
2246 /*@C
2247   PCFieldSplitGetISByIndex - Retrieves the elements for a given split as an `IS`
2248 
2249   Logically Collective
2250 
2251   Input Parameters:
2252 + pc    - the preconditioner context
2253 - index - index of this split
2254 
2255   Output Parameter:
2256 . is - the index set that defines the elements in this split
2257 
2258   Level: intermediate
2259 
2260 .seealso: [](sec_block_matrices), `PC`, `PCFieldSplitGetSubKSP()`, `PCFIELDSPLIT`, `PCFieldSplitGetIS()`, `PCFieldSplitSetIS()`
2261 @*/
2262 PetscErrorCode PCFieldSplitGetISByIndex(PC pc, PetscInt index, IS *is)
2263 {
2264   PetscFunctionBegin;
2265   PetscCheck(index >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Negative field %" PetscInt_FMT " requested", index);
2266   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
2267   PetscAssertPointer(is, 3);
2268   {
2269     PC_FieldSplit    *jac   = (PC_FieldSplit *)pc->data;
2270     PC_FieldSplitLink ilink = jac->head;
2271     PetscInt          i     = 0;
2272     PetscCheck(index < jac->nsplits, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Field %" PetscInt_FMT " requested but only %" PetscInt_FMT " exist", index, jac->nsplits);
2273 
2274     while (i < index) {
2275       ilink = ilink->next;
2276       ++i;
2277     }
2278     PetscCall(PCFieldSplitGetIS(pc, ilink->splitname, is));
2279   }
2280   PetscFunctionReturn(PETSC_SUCCESS);
2281 }
2282 
2283 /*@
2284   PCFieldSplitSetBlockSize - Sets the block size for defining where fields start in the
2285   fieldsplit preconditioner when calling `PCFieldSplitSetIS()`. If not set the matrix block size is used.
2286 
2287   Logically Collective
2288 
2289   Input Parameters:
2290 + pc - the preconditioner context
2291 - bs - the block size
2292 
2293   Level: intermediate
2294 
2295 .seealso: [](sec_block_matrices), `PC`, `PCFieldSplitGetSubKSP()`, `PCFIELDSPLIT`, `PCFieldSplitSetFields()`, `PCFieldSplitSetIS()`
2296 @*/
2297 PetscErrorCode PCFieldSplitSetBlockSize(PC pc, PetscInt bs)
2298 {
2299   PetscFunctionBegin;
2300   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
2301   PetscValidLogicalCollectiveInt(pc, bs, 2);
2302   PetscTryMethod(pc, "PCFieldSplitSetBlockSize_C", (PC, PetscInt), (pc, bs));
2303   PetscFunctionReturn(PETSC_SUCCESS);
2304 }
2305 
2306 /*@C
2307   PCFieldSplitGetSubKSP - Gets the `KSP` contexts for all splits
2308 
2309   Collective
2310 
2311   Input Parameter:
2312 . pc - the preconditioner context
2313 
2314   Output Parameters:
2315 + n      - the number of splits
2316 - subksp - the array of `KSP` contexts
2317 
2318   Level: advanced
2319 
2320   Notes:
2321   After `PCFieldSplitGetSubKSP()` the array of `KSP`s is to be freed by the user with `PetscFree()`
2322   (not the `KSP`, just the array that contains them).
2323 
2324   You must call `PCSetUp()` before calling `PCFieldSplitGetSubKSP()`.
2325 
2326   If the fieldsplit is of type `PC_COMPOSITE_SCHUR`, it returns the `KSP` object used inside the
2327   Schur complement and the `KSP` object used to iterate over the Schur complement.
2328   To access all the `KSP` objects used in `PC_COMPOSITE_SCHUR`, use `PCFieldSplitSchurGetSubKSP()`.
2329 
2330   If the fieldsplit is of type `PC_COMPOSITE_GKB`, it returns the `KSP` object used to solve the
2331   inner linear system defined by the matrix H in each loop.
2332 
2333   Fortran Notes:
2334   You must pass in a `KSP` array that is large enough to contain all the `KSP`s.
2335   You can call `PCFieldSplitGetSubKSP`(pc,n,`PETSC_NULL_KSP`,ierr) to determine how large the
2336   `KSP` array must be.
2337 
2338   Developer Notes:
2339   There should be a `PCFieldSplitRestoreSubKSP()` instead of requiring the user to call `PetscFree()`
2340 
2341   The Fortran interface should be modernized to return directly the array of values.
2342 
2343 .seealso: [](sec_block_matrices), `PC`, `PCFIELDSPLIT`, `PCFieldSplitSetFields()`, `PCFieldSplitSetIS()`, `PCFieldSplitSchurGetSubKSP()`
2344 @*/
2345 PetscErrorCode PCFieldSplitGetSubKSP(PC pc, PetscInt *n, KSP *subksp[])
2346 {
2347   PetscFunctionBegin;
2348   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
2349   if (n) PetscAssertPointer(n, 2);
2350   PetscUseMethod(pc, "PCFieldSplitGetSubKSP_C", (PC, PetscInt *, KSP **), (pc, n, subksp));
2351   PetscFunctionReturn(PETSC_SUCCESS);
2352 }
2353 
2354 /*@C
2355   PCFieldSplitSchurGetSubKSP - Gets the `KSP` contexts used inside the Schur complement based `PCFIELDSPLIT`
2356 
2357   Collective
2358 
2359   Input Parameter:
2360 . pc - the preconditioner context
2361 
2362   Output Parameters:
2363 + n      - the number of splits
2364 - subksp - the array of `KSP` contexts
2365 
2366   Level: advanced
2367 
2368   Notes:
2369   After `PCFieldSplitSchurGetSubKSP()` the array of `KSP`s is to be freed by the user with `PetscFree()`
2370   (not the `KSP` just the array that contains them).
2371 
2372   You must call `PCSetUp()` before calling `PCFieldSplitSchurGetSubKSP()`.
2373 
2374   If the fieldsplit type is of type `PC_COMPOSITE_SCHUR`, it returns (in order)
2375 +  1  - the `KSP` used for the (1,1) block
2376 .  2  - the `KSP` used for the Schur complement (not the one used for the interior Schur solver)
2377 -  3  - the `KSP` used for the (1,1) block in the upper triangular factor (if different from that of the (1,1) block).
2378 
2379   It returns a null array if the fieldsplit is not of type `PC_COMPOSITE_SCHUR`; in this case, you should use `PCFieldSplitGetSubKSP()`.
2380 
2381   Fortran Notes:
2382   You must pass in a `KSP` array that is large enough to contain all the local `KSP`s.
2383   You can call `PCFieldSplitSchurGetSubKSP`(pc,n,`PETSC_NULL_KSP`,ierr) to determine how large the
2384   `KSP` array must be.
2385 
2386   Developer Notes:
2387   There should be a `PCFieldSplitRestoreSubKSP()` instead of requiring the user to call `PetscFree()`
2388 
2389   Should the functionality of `PCFieldSplitSchurGetSubKSP()` and `PCFieldSplitGetSubKSP()` be merged?
2390 
2391   The Fortran interface should be modernized to return directly the array of values.
2392 
2393 .seealso: [](sec_block_matrices), `PC`, `PCFIELDSPLIT`, `PCFieldSplitSetFields()`, `PCFieldSplitSetIS()`, `PCFieldSplitGetSubKSP()`
2394 @*/
2395 PetscErrorCode PCFieldSplitSchurGetSubKSP(PC pc, PetscInt *n, KSP *subksp[])
2396 {
2397   PetscFunctionBegin;
2398   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
2399   if (n) PetscAssertPointer(n, 2);
2400   PetscUseMethod(pc, "PCFieldSplitSchurGetSubKSP_C", (PC, PetscInt *, KSP **), (pc, n, subksp));
2401   PetscFunctionReturn(PETSC_SUCCESS);
2402 }
2403 
2404 /*@
2405   PCFieldSplitSetSchurPre -  Indicates from what operator the preconditioner is constructucted for the Schur complement.
2406   The default is the A11 matrix.
2407 
2408   Collective
2409 
2410   Input Parameters:
2411 + pc    - the preconditioner context
2412 . ptype - which matrix to use for preconditioning the Schur complement: `PC_FIELDSPLIT_SCHUR_PRE_A11` (default),
2413               `PC_FIELDSPLIT_SCHUR_PRE_SELF`, `PC_FIELDSPLIT_SCHUR_PRE_USER`,
2414               `PC_FIELDSPLIT_SCHUR_PRE_SELFP`, and `PC_FIELDSPLIT_SCHUR_PRE_FULL`
2415 - pre   - matrix to use for preconditioning, or `NULL`
2416 
2417   Options Database Keys:
2418 + -pc_fieldsplit_schur_precondition <self,selfp,user,a11,full> - default is `a11`. See notes for meaning of various arguments
2419 - -fieldsplit_1_pc_type <pctype>                               - the preconditioner algorithm that is used to construct the preconditioner from the operator
2420 
2421   Level: intermediate
2422 
2423   Notes:
2424   If ptype is
2425 +     a11 - the preconditioner for the Schur complement is generated from the block diagonal part of the preconditioner
2426   matrix associated with the Schur complement (i.e. A11), not the Schur complement matrix
2427 .     self - the preconditioner for the Schur complement is generated from the symbolic representation of the Schur complement matrix:
2428   The only preconditioner that currently works with this symbolic representation matrix object is the `PCLSC`
2429   preconditioner
2430 .     user - the preconditioner for the Schur complement is generated from the user provided matrix (pre argument
2431   to this function).
2432 .     selfp - the preconditioning for the Schur complement is generated from an explicitly-assembled approximation Sp = A11 - A10 inv(diag(A00)) A01
2433   This is only a good preconditioner when diag(A00) is a good preconditioner for A00. Optionally, A00 can be
2434   lumped before extracting the diagonal using the additional option `-fieldsplit_1_mat_schur_complement_ainv_type lump`
2435 -     full - the preconditioner for the Schur complement is generated from the exact Schur complement matrix representation
2436   computed internally by `PCFIELDSPLIT` (this is expensive)
2437   useful mostly as a test that the Schur complement approach can work for your problem
2438 
2439   When solving a saddle point problem, where the A11 block is identically zero, using `a11` as the ptype only makes sense
2440   with the additional option `-fieldsplit_1_pc_type none`. Usually for saddle point problems one would use a ptype of self and
2441   `-fieldsplit_1_pc_type lsc` which uses the least squares commutator to compute a preconditioner for the Schur complement.
2442 
2443 .seealso: [](sec_block_matrices), `PC`, `PCFieldSplitGetSchurPre()`, `PCFieldSplitGetSubKSP()`, `PCFIELDSPLIT`, `PCFieldSplitSetFields()`, `PCFieldSplitSchurPreType`,
2444           `MatSchurComplementSetAinvType()`, `PCLSC`,
2445 
2446 @*/
2447 PetscErrorCode PCFieldSplitSetSchurPre(PC pc, PCFieldSplitSchurPreType ptype, Mat pre)
2448 {
2449   PetscFunctionBegin;
2450   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
2451   PetscTryMethod(pc, "PCFieldSplitSetSchurPre_C", (PC, PCFieldSplitSchurPreType, Mat), (pc, ptype, pre));
2452   PetscFunctionReturn(PETSC_SUCCESS);
2453 }
2454 
2455 PetscErrorCode PCFieldSplitSchurPrecondition(PC pc, PCFieldSplitSchurPreType ptype, Mat pre)
2456 {
2457   return PCFieldSplitSetSchurPre(pc, ptype, pre);
2458 } /* Deprecated name */
2459 
2460 /*@
2461   PCFieldSplitGetSchurPre - For Schur complement fieldsplit, determine how the Schur complement will be
2462   preconditioned.  See `PCFieldSplitSetSchurPre()` for details.
2463 
2464   Logically Collective
2465 
2466   Input Parameter:
2467 . pc - the preconditioner context
2468 
2469   Output Parameters:
2470 + ptype - which matrix to use for preconditioning the Schur complement: `PC_FIELDSPLIT_SCHUR_PRE_A11`, `PC_FIELDSPLIT_SCHUR_PRE_SELF`, `PC_FIELDSPLIT_SCHUR_PRE_USER`
2471 - pre   - matrix to use for preconditioning (with `PC_FIELDSPLIT_SCHUR_PRE_USER`), or `NULL`
2472 
2473   Level: intermediate
2474 
2475 .seealso: [](sec_block_matrices), `PC`, `PCFieldSplitSetSchurPre()`, `PCFieldSplitGetSubKSP()`, `PCFIELDSPLIT`, `PCFieldSplitSetFields()`, `PCFieldSplitSchurPreType`, `PCLSC`
2476 
2477 @*/
2478 PetscErrorCode PCFieldSplitGetSchurPre(PC pc, PCFieldSplitSchurPreType *ptype, Mat *pre)
2479 {
2480   PetscFunctionBegin;
2481   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
2482   PetscUseMethod(pc, "PCFieldSplitGetSchurPre_C", (PC, PCFieldSplitSchurPreType *, Mat *), (pc, ptype, pre));
2483   PetscFunctionReturn(PETSC_SUCCESS);
2484 }
2485 
2486 /*@
2487   PCFieldSplitSchurGetS -  extract the `MATSCHURCOMPLEMENT` object used by this `PCFIELDSPLIT` in case it needs to be configured separately
2488 
2489   Not Collective
2490 
2491   Input Parameter:
2492 . pc - the preconditioner context
2493 
2494   Output Parameter:
2495 . S - the Schur complement matrix
2496 
2497   Level: advanced
2498 
2499   Note:
2500   This matrix should not be destroyed using `MatDestroy()`; rather, use `PCFieldSplitSchurRestoreS()`.
2501 
2502 .seealso: [](sec_block_matrices), `PC`, `PCFieldSplitGetSubKSP()`, `PCFIELDSPLIT`, `PCFieldSplitSchurPreType`, `PCFieldSplitSetSchurPre()`, `MATSCHURCOMPLEMENT`, `PCFieldSplitSchurRestoreS()`,
2503           `MatCreateSchurComplement()`, `MatSchurComplementGetKSP()`, `MatSchurComplementComputeExplicitOperator()`, `MatGetSchurComplement()`
2504 @*/
2505 PetscErrorCode PCFieldSplitSchurGetS(PC pc, Mat *S)
2506 {
2507   const char    *t;
2508   PetscBool      isfs;
2509   PC_FieldSplit *jac;
2510 
2511   PetscFunctionBegin;
2512   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
2513   PetscCall(PetscObjectGetType((PetscObject)pc, &t));
2514   PetscCall(PetscStrcmp(t, PCFIELDSPLIT, &isfs));
2515   PetscCheck(isfs, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Expected PC of type PCFIELDSPLIT, got %s instead", t);
2516   jac = (PC_FieldSplit *)pc->data;
2517   PetscCheck(jac->type == PC_COMPOSITE_SCHUR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Expected PCFIELDSPLIT of type SCHUR, got %d instead", jac->type);
2518   if (S) *S = jac->schur;
2519   PetscFunctionReturn(PETSC_SUCCESS);
2520 }
2521 
2522 /*@
2523   PCFieldSplitSchurRestoreS -  returns the `MATSCHURCOMPLEMENT` matrix used by this `PC`
2524 
2525   Not Collective
2526 
2527   Input Parameters:
2528 + pc - the preconditioner context
2529 - S  - the Schur complement matrix
2530 
2531   Level: advanced
2532 
2533 .seealso: [](sec_block_matrices), `PC`, `PCFieldSplitGetSubKSP()`, `PCFIELDSPLIT`, `PCFieldSplitSchurPreType`, `PCFieldSplitSetSchurPre()`, `MatSchurComplement`, `PCFieldSplitSchurGetS()`
2534 @*/
2535 PetscErrorCode PCFieldSplitSchurRestoreS(PC pc, Mat *S)
2536 {
2537   const char    *t;
2538   PetscBool      isfs;
2539   PC_FieldSplit *jac;
2540 
2541   PetscFunctionBegin;
2542   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
2543   PetscCall(PetscObjectGetType((PetscObject)pc, &t));
2544   PetscCall(PetscStrcmp(t, PCFIELDSPLIT, &isfs));
2545   PetscCheck(isfs, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Expected PC of type PCFIELDSPLIT, got %s instead", t);
2546   jac = (PC_FieldSplit *)pc->data;
2547   PetscCheck(jac->type == PC_COMPOSITE_SCHUR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Expected PCFIELDSPLIT of type SCHUR, got %d instead", jac->type);
2548   PetscCheck(S && (*S == jac->schur), PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "MatSchurComplement restored is not the same as gotten");
2549   PetscFunctionReturn(PETSC_SUCCESS);
2550 }
2551 
2552 static PetscErrorCode PCFieldSplitSetSchurPre_FieldSplit(PC pc, PCFieldSplitSchurPreType ptype, Mat pre)
2553 {
2554   PC_FieldSplit *jac = (PC_FieldSplit *)pc->data;
2555 
2556   PetscFunctionBegin;
2557   jac->schurpre = ptype;
2558   if (ptype == PC_FIELDSPLIT_SCHUR_PRE_USER && pre) {
2559     PetscCall(MatDestroy(&jac->schur_user));
2560     jac->schur_user = pre;
2561     PetscCall(PetscObjectReference((PetscObject)jac->schur_user));
2562   }
2563   PetscFunctionReturn(PETSC_SUCCESS);
2564 }
2565 
2566 static PetscErrorCode PCFieldSplitGetSchurPre_FieldSplit(PC pc, PCFieldSplitSchurPreType *ptype, Mat *pre)
2567 {
2568   PC_FieldSplit *jac = (PC_FieldSplit *)pc->data;
2569 
2570   PetscFunctionBegin;
2571   if (ptype) *ptype = jac->schurpre;
2572   if (pre) *pre = jac->schur_user;
2573   PetscFunctionReturn(PETSC_SUCCESS);
2574 }
2575 
2576 /*@
2577   PCFieldSplitSetSchurFactType -  sets which blocks of the approximate block factorization to retain in the preconditioner
2578 
2579   Collective
2580 
2581   Input Parameters:
2582 + pc    - the preconditioner context
2583 - ftype - which blocks of factorization to retain, `PC_FIELDSPLIT_SCHUR_FACT_FULL` is default
2584 
2585   Options Database Key:
2586 . -pc_fieldsplit_schur_fact_type <diag,lower,upper,full> - default is `full`
2587 
2588   Level: intermediate
2589 
2590   Notes:
2591   The FULL factorization is
2592 
2593 .vb
2594    (A   B)  = (1       0) (A   0) (1  Ainv*B)  = L D U
2595    (C   E)    (C*Ainv  1) (0   S) (0       1)
2596 .vb
2597     where S = E - C*Ainv*B. In practice, the full factorization is applied via block triangular solves with the grouping L*(D*U). UPPER uses D*U, LOWER uses L*D,
2598     and DIAG is the diagonal part with the sign of S flipped (because this makes the preconditioner positive definite for many formulations, thus allowing the use of `KSPMINRES)`.
2599     Sign flipping of S can be turned off with `PCFieldSplitSetSchurScale()`.
2600 
2601     If A and S are solved exactly
2602 .vb
2603       *) FULL factorization is a direct solver.
2604       *) The preconditioned operator with LOWER or UPPER has all eigenvalues equal to 1 and minimal polynomial of degree 2, so `KSPGMRES` converges in 2 iterations.
2605       *) With DIAG, the preconditioned operator has three distinct nonzero eigenvalues and minimal polynomial of degree at most 4, so `KSPGMRES` converges in at most 4 iterations.
2606 .ve
2607 
2608   If the iteration count is very low, consider using `KSPFGMRES` or `KSPGCR` which can use one less preconditioner
2609   application in this case. Note that the preconditioned operator may be highly non-normal, so such fast convergence may not be observed in practice.
2610 
2611   For symmetric problems in which A is positive definite and S is negative definite, DIAG can be used with `KSPMINRES`.
2612 
2613   A flexible method like `KSPFGMRES` or `KSPGCR` must be used if the fieldsplit preconditioner is nonlinear (e.g. a few iterations of a Krylov method is used to solve with A or S).
2614 
2615   References:
2616 +   * - Murphy, Golub, and Wathen, A note on preconditioning indefinite linear systems, SIAM J. Sci. Comput., 21 (2000).
2617 -   * - Ipsen, A note on preconditioning nonsymmetric matrices, SIAM J. Sci. Comput., 23 (2001).
2618 
2619 .seealso: [](sec_block_matrices), `PC`, `PCFieldSplitGetSubKSP()`, `PCFIELDSPLIT`, `PCFieldSplitSetFields()`, `PCFieldSplitSchurPreType`, `PCFieldSplitSetSchurScale()`
2620 @*/
2621 PetscErrorCode PCFieldSplitSetSchurFactType(PC pc, PCFieldSplitSchurFactType ftype)
2622 {
2623   PetscFunctionBegin;
2624   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
2625   PetscTryMethod(pc, "PCFieldSplitSetSchurFactType_C", (PC, PCFieldSplitSchurFactType), (pc, ftype));
2626   PetscFunctionReturn(PETSC_SUCCESS);
2627 }
2628 
2629 static PetscErrorCode PCFieldSplitSetSchurFactType_FieldSplit(PC pc, PCFieldSplitSchurFactType ftype)
2630 {
2631   PC_FieldSplit *jac = (PC_FieldSplit *)pc->data;
2632 
2633   PetscFunctionBegin;
2634   jac->schurfactorization = ftype;
2635   PetscFunctionReturn(PETSC_SUCCESS);
2636 }
2637 
2638 /*@
2639   PCFieldSplitSetSchurScale -  Controls the sign flip of S for `PC_FIELDSPLIT_SCHUR_FACT_DIAG`.
2640 
2641   Collective
2642 
2643   Input Parameters:
2644 + pc    - the preconditioner context
2645 - scale - scaling factor for the Schur complement
2646 
2647   Options Database Key:
2648 . -pc_fieldsplit_schur_scale - default is -1.0
2649 
2650   Level: intermediate
2651 
2652 .seealso: [](sec_block_matrices), `PC`, `PCFIELDSPLIT`, `PCFieldSplitSetFields()`, `PCFieldSplitSchurFactType`, `PCFieldSplitSetSchurFactType()`
2653 @*/
2654 PetscErrorCode PCFieldSplitSetSchurScale(PC pc, PetscScalar scale)
2655 {
2656   PetscFunctionBegin;
2657   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
2658   PetscValidLogicalCollectiveScalar(pc, scale, 2);
2659   PetscTryMethod(pc, "PCFieldSplitSetSchurScale_C", (PC, PetscScalar), (pc, scale));
2660   PetscFunctionReturn(PETSC_SUCCESS);
2661 }
2662 
2663 static PetscErrorCode PCFieldSplitSetSchurScale_FieldSplit(PC pc, PetscScalar scale)
2664 {
2665   PC_FieldSplit *jac = (PC_FieldSplit *)pc->data;
2666 
2667   PetscFunctionBegin;
2668   jac->schurscale = scale;
2669   PetscFunctionReturn(PETSC_SUCCESS);
2670 }
2671 
2672 /*@C
2673   PCFieldSplitGetSchurBlocks - Gets all matrix blocks for the Schur complement
2674 
2675   Collective
2676 
2677   Input Parameter:
2678 . pc - the preconditioner context
2679 
2680   Output Parameters:
2681 + A00 - the (0,0) block
2682 . A01 - the (0,1) block
2683 . A10 - the (1,0) block
2684 - A11 - the (1,1) block
2685 
2686   Level: advanced
2687 
2688 .seealso: [](sec_block_matrices), `PC`, `PCFIELDSPLIT`, `MatSchurComplementGetSubMatrices()`, `MatSchurComplementSetSubMatrices()`
2689 @*/
2690 PetscErrorCode PCFieldSplitGetSchurBlocks(PC pc, Mat *A00, Mat *A01, Mat *A10, Mat *A11)
2691 {
2692   PC_FieldSplit *jac = (PC_FieldSplit *)pc->data;
2693 
2694   PetscFunctionBegin;
2695   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
2696   PetscCheck(jac->type == PC_COMPOSITE_SCHUR, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_WRONG, "FieldSplit is not using a Schur complement approach.");
2697   if (A00) *A00 = jac->pmat[0];
2698   if (A01) *A01 = jac->B;
2699   if (A10) *A10 = jac->C;
2700   if (A11) *A11 = jac->pmat[1];
2701   PetscFunctionReturn(PETSC_SUCCESS);
2702 }
2703 
2704 /*@
2705   PCFieldSplitSetGKBTol -  Sets the solver tolerance for the generalized Golub-Kahan bidiagonalization preconditioner in `PCFIELDSPLIT`
2706 
2707   Collective
2708 
2709   Input Parameters:
2710 + pc        - the preconditioner context
2711 - tolerance - the solver tolerance
2712 
2713   Options Database Key:
2714 . -pc_fieldsplit_gkb_tol - default is 1e-5
2715 
2716   Level: intermediate
2717 
2718   Note:
2719   The generalized GKB algorithm uses a lower bound estimate of the error in energy norm as stopping criterion.
2720   It stops once the lower bound estimate undershoots the required solver tolerance. Although the actual error might be bigger than
2721   this estimate, the stopping criterion is satisfactory in practical cases [A13].
2722 
2723   References:
2724   [Ar13] Generalized Golub-Kahan bidiagonalization and stopping criteria, SIAM J. Matrix Anal. Appl., Vol. 34, No. 2, pp. 571-592, 2013.
2725 
2726 .seealso: [](sec_block_matrices), `PC`, `PCFIELDSPLIT`, `PCFieldSplitSetGKBDelay()`, `PCFieldSplitSetGKBNu()`, `PCFieldSplitSetGKBMaxit()`
2727 @*/
2728 PetscErrorCode PCFieldSplitSetGKBTol(PC pc, PetscReal tolerance)
2729 {
2730   PetscFunctionBegin;
2731   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
2732   PetscValidLogicalCollectiveReal(pc, tolerance, 2);
2733   PetscTryMethod(pc, "PCFieldSplitSetGKBTol_C", (PC, PetscReal), (pc, tolerance));
2734   PetscFunctionReturn(PETSC_SUCCESS);
2735 }
2736 
2737 static PetscErrorCode PCFieldSplitSetGKBTol_FieldSplit(PC pc, PetscReal tolerance)
2738 {
2739   PC_FieldSplit *jac = (PC_FieldSplit *)pc->data;
2740 
2741   PetscFunctionBegin;
2742   jac->gkbtol = tolerance;
2743   PetscFunctionReturn(PETSC_SUCCESS);
2744 }
2745 
2746 /*@
2747   PCFieldSplitSetGKBMaxit -  Sets the maximum number of iterations for the generalized Golub-Kahan bidiagonalization preconditioner in `PCFIELDSPLIT`
2748 
2749   Collective
2750 
2751   Input Parameters:
2752 + pc    - the preconditioner context
2753 - maxit - the maximum number of iterations
2754 
2755   Options Database Key:
2756 . -pc_fieldsplit_gkb_maxit - default is 100
2757 
2758   Level: intermediate
2759 
2760 .seealso: [](sec_block_matrices), `PC`, `PCFIELDSPLIT`, `PCFieldSplitSetGKBDelay()`, `PCFieldSplitSetGKBTol()`, `PCFieldSplitSetGKBNu()`
2761 @*/
2762 PetscErrorCode PCFieldSplitSetGKBMaxit(PC pc, PetscInt maxit)
2763 {
2764   PetscFunctionBegin;
2765   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
2766   PetscValidLogicalCollectiveInt(pc, maxit, 2);
2767   PetscTryMethod(pc, "PCFieldSplitSetGKBMaxit_C", (PC, PetscInt), (pc, maxit));
2768   PetscFunctionReturn(PETSC_SUCCESS);
2769 }
2770 
2771 static PetscErrorCode PCFieldSplitSetGKBMaxit_FieldSplit(PC pc, PetscInt maxit)
2772 {
2773   PC_FieldSplit *jac = (PC_FieldSplit *)pc->data;
2774 
2775   PetscFunctionBegin;
2776   jac->gkbmaxit = maxit;
2777   PetscFunctionReturn(PETSC_SUCCESS);
2778 }
2779 
2780 /*@
2781   PCFieldSplitSetGKBDelay -  Sets the delay in the lower bound error estimate in the generalized Golub-Kahan bidiagonalization in `PCFIELDSPLIT`
2782   preconditioner.
2783 
2784   Collective
2785 
2786   Input Parameters:
2787 + pc    - the preconditioner context
2788 - delay - the delay window in the lower bound estimate
2789 
2790   Options Database Key:
2791 . -pc_fieldsplit_gkb_delay - default is 5
2792 
2793   Level: intermediate
2794 
2795   Note:
2796   The algorithm uses a lower bound estimate of the error in energy norm as stopping criterion. The lower bound of the error ||u-u^k||_H
2797   is expressed as a truncated sum. The error at iteration k can only be measured at iteration (k + delay), and thus the algorithm needs
2798   at least (delay + 1) iterations to stop. For more details on the generalized Golub-Kahan bidiagonalization method and its lower bound stopping criterion, please refer to
2799 
2800   References:
2801   [Ar13] Generalized Golub-Kahan bidiagonalization and stopping criteria, SIAM J. Matrix Anal. Appl., Vol. 34, No. 2, pp. 571-592, 2013.
2802 
2803 .seealso: [](sec_block_matrices), `PC`, `PCFIELDSPLIT`, `PCFieldSplitSetGKBNu()`, `PCFieldSplitSetGKBTol()`, `PCFieldSplitSetGKBMaxit()`
2804 @*/
2805 PetscErrorCode PCFieldSplitSetGKBDelay(PC pc, PetscInt delay)
2806 {
2807   PetscFunctionBegin;
2808   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
2809   PetscValidLogicalCollectiveInt(pc, delay, 2);
2810   PetscTryMethod(pc, "PCFieldSplitSetGKBDelay_C", (PC, PetscInt), (pc, delay));
2811   PetscFunctionReturn(PETSC_SUCCESS);
2812 }
2813 
2814 static PetscErrorCode PCFieldSplitSetGKBDelay_FieldSplit(PC pc, PetscInt delay)
2815 {
2816   PC_FieldSplit *jac = (PC_FieldSplit *)pc->data;
2817 
2818   PetscFunctionBegin;
2819   jac->gkbdelay = delay;
2820   PetscFunctionReturn(PETSC_SUCCESS);
2821 }
2822 
2823 /*@
2824   PCFieldSplitSetGKBNu -  Sets the scalar value nu >= 0 in the transformation H = A00 + nu*A01*A01' of the (1,1) block in the Golub-Kahan bidiagonalization preconditioner
2825   in `PCFIELDSPLIT`
2826 
2827   Collective
2828 
2829   Input Parameters:
2830 + pc - the preconditioner context
2831 - nu - the shift parameter
2832 
2833   Options Database Key:
2834 . -pc_fieldsplit_gkb_nu - default is 1
2835 
2836   Level: intermediate
2837 
2838   Notes:
2839   This shift is in general done to obtain better convergence properties for the outer loop of the algorithm. This is often achieved by choosing nu sufficiently big. However,
2840   if nu is chosen too big, the matrix H might be badly conditioned and the solution of the linear system Hx = b in the inner loop becomes difficult. It is therefore
2841   necessary to find a good balance in between the convergence of the inner and outer loop.
2842 
2843   For nu = 0, no shift is done. In this case A00 has to be positive definite. The matrix N in [Ar13] is then chosen as identity.
2844 
2845   References:
2846   [Ar13] Generalized Golub-Kahan bidiagonalization and stopping criteria, SIAM J. Matrix Anal. Appl., Vol. 34, No. 2, pp. 571-592, 2013.
2847 
2848 .seealso: [](sec_block_matrices), `PC`, `PCFIELDSPLIT`, `PCFieldSplitSetGKBDelay()`, `PCFieldSplitSetGKBTol()`, `PCFieldSplitSetGKBMaxit()`
2849 @*/
2850 PetscErrorCode PCFieldSplitSetGKBNu(PC pc, PetscReal nu)
2851 {
2852   PetscFunctionBegin;
2853   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
2854   PetscValidLogicalCollectiveReal(pc, nu, 2);
2855   PetscTryMethod(pc, "PCFieldSplitSetGKBNu_C", (PC, PetscReal), (pc, nu));
2856   PetscFunctionReturn(PETSC_SUCCESS);
2857 }
2858 
2859 static PetscErrorCode PCFieldSplitSetGKBNu_FieldSplit(PC pc, PetscReal nu)
2860 {
2861   PC_FieldSplit *jac = (PC_FieldSplit *)pc->data;
2862 
2863   PetscFunctionBegin;
2864   jac->gkbnu = nu;
2865   PetscFunctionReturn(PETSC_SUCCESS);
2866 }
2867 
2868 static PetscErrorCode PCFieldSplitSetType_FieldSplit(PC pc, PCCompositeType type)
2869 {
2870   PC_FieldSplit *jac = (PC_FieldSplit *)pc->data;
2871 
2872   PetscFunctionBegin;
2873   jac->type = type;
2874   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitGetSubKSP_C", NULL));
2875   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetSchurPre_C", NULL));
2876   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitGetSchurPre_C", NULL));
2877   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetSchurFactType_C", NULL));
2878   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetSchurScale_C", NULL));
2879   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetGKBTol_C", NULL));
2880   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetGKBMaxit_C", NULL));
2881   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetGKBNu_C", NULL));
2882   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetGKBDelay_C", NULL));
2883 
2884   if (type == PC_COMPOSITE_SCHUR) {
2885     pc->ops->apply          = PCApply_FieldSplit_Schur;
2886     pc->ops->applytranspose = PCApplyTranspose_FieldSplit_Schur;
2887     pc->ops->view           = PCView_FieldSplit_Schur;
2888 
2889     PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitGetSubKSP_C", PCFieldSplitGetSubKSP_FieldSplit_Schur));
2890     PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetSchurPre_C", PCFieldSplitSetSchurPre_FieldSplit));
2891     PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitGetSchurPre_C", PCFieldSplitGetSchurPre_FieldSplit));
2892     PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetSchurFactType_C", PCFieldSplitSetSchurFactType_FieldSplit));
2893     PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetSchurScale_C", PCFieldSplitSetSchurScale_FieldSplit));
2894   } else if (type == PC_COMPOSITE_GKB) {
2895     pc->ops->apply = PCApply_FieldSplit_GKB;
2896     pc->ops->view  = PCView_FieldSplit_GKB;
2897 
2898     PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitGetSubKSP_C", PCFieldSplitGetSubKSP_FieldSplit));
2899     PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetGKBTol_C", PCFieldSplitSetGKBTol_FieldSplit));
2900     PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetGKBMaxit_C", PCFieldSplitSetGKBMaxit_FieldSplit));
2901     PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetGKBNu_C", PCFieldSplitSetGKBNu_FieldSplit));
2902     PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetGKBDelay_C", PCFieldSplitSetGKBDelay_FieldSplit));
2903   } else {
2904     pc->ops->apply = PCApply_FieldSplit;
2905     pc->ops->view  = PCView_FieldSplit;
2906 
2907     PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitGetSubKSP_C", PCFieldSplitGetSubKSP_FieldSplit));
2908   }
2909   PetscFunctionReturn(PETSC_SUCCESS);
2910 }
2911 
2912 static PetscErrorCode PCFieldSplitSetBlockSize_FieldSplit(PC pc, PetscInt bs)
2913 {
2914   PC_FieldSplit *jac = (PC_FieldSplit *)pc->data;
2915 
2916   PetscFunctionBegin;
2917   PetscCheck(bs >= 1, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_OUTOFRANGE, "Blocksize must be positive, you gave %" PetscInt_FMT, bs);
2918   PetscCheck(jac->bs <= 0 || jac->bs == bs, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_WRONGSTATE, "Cannot change fieldsplit blocksize from %" PetscInt_FMT " to %" PetscInt_FMT " after it has been set", jac->bs, bs);
2919   jac->bs = bs;
2920   PetscFunctionReturn(PETSC_SUCCESS);
2921 }
2922 
2923 static PetscErrorCode PCSetCoordinates_FieldSplit(PC pc, PetscInt dim, PetscInt nloc, PetscReal coords[])
2924 {
2925   PC_FieldSplit    *jac           = (PC_FieldSplit *)pc->data;
2926   PC_FieldSplitLink ilink_current = jac->head;
2927   IS                is_owned;
2928 
2929   PetscFunctionBegin;
2930   jac->coordinates_set = PETSC_TRUE; // Internal flag
2931   PetscCall(MatGetOwnershipIS(pc->mat, &is_owned, NULL));
2932 
2933   while (ilink_current) {
2934     // For each IS, embed it to get local coords indces
2935     IS              is_coords;
2936     PetscInt        ndofs_block;
2937     const PetscInt *block_dofs_enumeration; // Numbering of the dofs relevant to the current block
2938 
2939     // Setting drop to true for safety. It should make no difference.
2940     PetscCall(ISEmbed(ilink_current->is, is_owned, PETSC_TRUE, &is_coords));
2941     PetscCall(ISGetLocalSize(is_coords, &ndofs_block));
2942     PetscCall(ISGetIndices(is_coords, &block_dofs_enumeration));
2943 
2944     // Allocate coordinates vector and set it directly
2945     PetscCall(PetscMalloc1(ndofs_block * dim, &(ilink_current->coords)));
2946     for (PetscInt dof = 0; dof < ndofs_block; ++dof) {
2947       for (PetscInt d = 0; d < dim; ++d) (ilink_current->coords)[dim * dof + d] = coords[dim * block_dofs_enumeration[dof] + d];
2948     }
2949     ilink_current->dim   = dim;
2950     ilink_current->ndofs = ndofs_block;
2951     PetscCall(ISRestoreIndices(is_coords, &block_dofs_enumeration));
2952     PetscCall(ISDestroy(&is_coords));
2953     ilink_current = ilink_current->next;
2954   }
2955   PetscCall(ISDestroy(&is_owned));
2956   PetscFunctionReturn(PETSC_SUCCESS);
2957 }
2958 
2959 /*@
2960   PCFieldSplitSetType - Sets the type, `PCCompositeType`, of a `PCFIELDSPLIT`
2961 
2962   Collective
2963 
2964   Input Parameters:
2965 + pc   - the preconditioner context
2966 - type - `PC_COMPOSITE_ADDITIVE`, `PC_COMPOSITE_MULTIPLICATIVE` (default), `PC_COMPOSITE_SYMMETRIC_MULTIPLICATIVE`, `PC_COMPOSITE_SPECIAL`, `PC_COMPOSITE_SCHUR`
2967 
2968   Options Database Key:
2969 . -pc_fieldsplit_type <type: one of multiplicative, additive, symmetric_multiplicative, special, schur> - Sets fieldsplit preconditioner type
2970 
2971   Level: intermediate
2972 
2973 .seealso: [](sec_block_matrices), `PC`, `PCFIELDSPLIT`, `PCCompositeType`, `PCCompositeGetType()`, `PC_COMPOSITE_ADDITIVE`, `PC_COMPOSITE_MULTIPLICATIVE`,
2974           `PC_COMPOSITE_SYMMETRIC_MULTIPLICATIVE`, `PC_COMPOSITE_SPECIAL`, `PC_COMPOSITE_SCHUR`
2975 @*/
2976 PetscErrorCode PCFieldSplitSetType(PC pc, PCCompositeType type)
2977 {
2978   PetscFunctionBegin;
2979   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
2980   PetscTryMethod(pc, "PCFieldSplitSetType_C", (PC, PCCompositeType), (pc, type));
2981   PetscFunctionReturn(PETSC_SUCCESS);
2982 }
2983 
2984 /*@
2985   PCFieldSplitGetType - Gets the type, `PCCompositeType`, of a `PCFIELDSPLIT`
2986 
2987   Not collective
2988 
2989   Input Parameter:
2990 . pc - the preconditioner context
2991 
2992   Output Parameter:
2993 . type - `PC_COMPOSITE_ADDITIVE`, `PC_COMPOSITE_MULTIPLICATIVE` (default), `PC_COMPOSITE_SYMMETRIC_MULTIPLICATIVE`, `PC_COMPOSITE_SPECIAL`, `PC_COMPOSITE_SCHUR`
2994 
2995   Level: intermediate
2996 
2997 .seealso: [](sec_block_matrices), `PC`, `PCCompositeSetType()`, `PCFIELDSPLIT`, `PCCompositeType`, `PC_COMPOSITE_ADDITIVE`, `PC_COMPOSITE_MULTIPLICATIVE`,
2998           `PC_COMPOSITE_SYMMETRIC_MULTIPLICATIVE`, `PC_COMPOSITE_SPECIAL`, `PC_COMPOSITE_SCHUR`
2999 @*/
3000 PetscErrorCode PCFieldSplitGetType(PC pc, PCCompositeType *type)
3001 {
3002   PC_FieldSplit *jac = (PC_FieldSplit *)pc->data;
3003 
3004   PetscFunctionBegin;
3005   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
3006   PetscAssertPointer(type, 2);
3007   *type = jac->type;
3008   PetscFunctionReturn(PETSC_SUCCESS);
3009 }
3010 
3011 /*@
3012   PCFieldSplitSetDMSplits - Flags whether `DMCreateFieldDecomposition()` should be used to define the splits in a `PCFIELDSPLIT`, whenever possible.
3013 
3014   Logically Collective
3015 
3016   Input Parameters:
3017 + pc  - the preconditioner context
3018 - flg - boolean indicating whether to use field splits defined by the `DM`
3019 
3020   Options Database Key:
3021 . -pc_fieldsplit_dm_splits <bool> - use the field splits defined by the `DM`
3022 
3023   Level: intermediate
3024 
3025 .seealso: [](sec_block_matrices), `PC`, `PCFIELDSPLIT`, `PCFieldSplitGetDMSplits()`, `PCFieldSplitSetFields()`, `PCFieldsplitSetIS()`
3026 @*/
3027 PetscErrorCode PCFieldSplitSetDMSplits(PC pc, PetscBool flg)
3028 {
3029   PC_FieldSplit *jac = (PC_FieldSplit *)pc->data;
3030   PetscBool      isfs;
3031 
3032   PetscFunctionBegin;
3033   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
3034   PetscValidLogicalCollectiveBool(pc, flg, 2);
3035   PetscCall(PetscObjectTypeCompare((PetscObject)pc, PCFIELDSPLIT, &isfs));
3036   if (isfs) jac->dm_splits = flg;
3037   PetscFunctionReturn(PETSC_SUCCESS);
3038 }
3039 
3040 /*@
3041   PCFieldSplitGetDMSplits - Returns flag indicating whether `DMCreateFieldDecomposition()` should be used to define the splits in a `PCFIELDSPLIT`, whenever possible.
3042 
3043   Logically Collective
3044 
3045   Input Parameter:
3046 . pc - the preconditioner context
3047 
3048   Output Parameter:
3049 . flg - boolean indicating whether to use field splits defined by the `DM`
3050 
3051   Level: intermediate
3052 
3053 .seealso: [](sec_block_matrices), `PC`, `PCFIELDSPLIT`, `PCFieldSplitSetDMSplits()`, `PCFieldSplitSetFields()`, `PCFieldsplitSetIS()`
3054 @*/
3055 PetscErrorCode PCFieldSplitGetDMSplits(PC pc, PetscBool *flg)
3056 {
3057   PC_FieldSplit *jac = (PC_FieldSplit *)pc->data;
3058   PetscBool      isfs;
3059 
3060   PetscFunctionBegin;
3061   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
3062   PetscAssertPointer(flg, 2);
3063   PetscCall(PetscObjectTypeCompare((PetscObject)pc, PCFIELDSPLIT, &isfs));
3064   if (isfs) {
3065     if (flg) *flg = jac->dm_splits;
3066   }
3067   PetscFunctionReturn(PETSC_SUCCESS);
3068 }
3069 
3070 /*@
3071   PCFieldSplitGetDetectSaddlePoint - Returns flag indicating whether `PCFIELDSPLIT` will attempt to automatically determine fields based on zero diagonal entries.
3072 
3073   Logically Collective
3074 
3075   Input Parameter:
3076 . pc - the preconditioner context
3077 
3078   Output Parameter:
3079 . flg - boolean indicating whether to detect fields or not
3080 
3081   Level: intermediate
3082 
3083 .seealso: [](sec_block_matrices), `PC`, `PCFIELDSPLIT`, `PCFieldSplitSetDetectSaddlePoint()`
3084 @*/
3085 PetscErrorCode PCFieldSplitGetDetectSaddlePoint(PC pc, PetscBool *flg)
3086 {
3087   PC_FieldSplit *jac = (PC_FieldSplit *)pc->data;
3088 
3089   PetscFunctionBegin;
3090   *flg = jac->detect;
3091   PetscFunctionReturn(PETSC_SUCCESS);
3092 }
3093 
3094 /*@
3095   PCFieldSplitSetDetectSaddlePoint - Sets flag indicating whether `PCFIELDSPLIT` will attempt to automatically determine fields based on zero diagonal entries.
3096 
3097   Logically Collective
3098 
3099   Input Parameter:
3100 . pc - the preconditioner context
3101 
3102   Output Parameter:
3103 . flg - boolean indicating whether to detect fields or not
3104 
3105   Options Database Key:
3106 . -pc_fieldsplit_detect_saddle_point <bool> - detect and use the saddle point
3107 
3108   Level: intermediate
3109 
3110   Note:
3111   Also sets the split type to `PC_COMPOSITE_SCHUR` (see `PCFieldSplitSetType()`) and the Schur preconditioner type to `PC_FIELDSPLIT_SCHUR_PRE_SELF` (see `PCFieldSplitSetSchurPre()`).
3112 
3113 .seealso: [](sec_block_matrices), `PC`, `PCFIELDSPLIT`, `PCFieldSplitGetDetectSaddlePoint()`, `PCFieldSplitSetType()`, `PCFieldSplitSetSchurPre()`, `PC_FIELDSPLIT_SCHUR_PRE_SELF`
3114 @*/
3115 PetscErrorCode PCFieldSplitSetDetectSaddlePoint(PC pc, PetscBool flg)
3116 {
3117   PC_FieldSplit *jac = (PC_FieldSplit *)pc->data;
3118 
3119   PetscFunctionBegin;
3120   jac->detect = flg;
3121   if (jac->detect) {
3122     PetscCall(PCFieldSplitSetType(pc, PC_COMPOSITE_SCHUR));
3123     PetscCall(PCFieldSplitSetSchurPre(pc, PC_FIELDSPLIT_SCHUR_PRE_SELF, NULL));
3124   }
3125   PetscFunctionReturn(PETSC_SUCCESS);
3126 }
3127 
3128 /*MC
3129    PCFIELDSPLIT - Preconditioner created by combining separate preconditioners for individual
3130    collections of variables (that may overlap) called splits. See [the users manual section on "Solving Block Matrices"](sec_block_matrices) for more details.
3131 
3132    Options Database Keys:
3133 +   -pc_fieldsplit_%d_fields <a,b,..> - indicates the fields to be used in the `%d`'th split
3134 .   -pc_fieldsplit_default - automatically add any fields to additional splits that have not
3135                               been supplied explicitly by `-pc_fieldsplit_%d_fields`
3136 .   -pc_fieldsplit_block_size <bs> - size of block that defines fields (i.e. there are bs fields)
3137 .   -pc_fieldsplit_type <additive,multiplicative,symmetric_multiplicative,schur,gkb> - type of relaxation or factorization splitting
3138 .   -pc_fieldsplit_schur_precondition <self,selfp,user,a11,full> - default is `a11`; see `PCFieldSplitSetSchurPre()`
3139 .   -pc_fieldsplit_schur_fact_type <diag,lower,upper,full> - set factorization type when using `-pc_fieldsplit_type schur`; see `PCFieldSplitSetSchurFactType()`
3140 -   -pc_fieldsplit_detect_saddle_point - automatically finds rows with zero diagonal and uses Schur complement with no preconditioner as the solver
3141 
3142      Options prefixes for inner solvers when using the Schur complement preconditioner are `-fieldsplit_0_` and `-fieldsplit_1_` .
3143      The options prefix for the inner solver when using the Golub-Kahan biadiagonalization preconditioner is `-fieldsplit_0_`
3144      For all other solvers they are `-fieldsplit_%d_` for the `%d`'th field; use `-fieldsplit_` for all fields.
3145 
3146      To set options on the solvers for each block append `-fieldsplit_` to all the `PC`
3147      options database keys. For example, `-fieldsplit_pc_type ilu` `-fieldsplit_pc_factor_levels 1`
3148 
3149      To set the options on the solvers separate for each block call `PCFieldSplitGetSubKSP()`
3150       and set the options directly on the resulting `KSP` object
3151 
3152     Level: intermediate
3153 
3154    Notes:
3155     Use `PCFieldSplitSetFields()` to set splits defined by "strided" entries and `PCFieldSplitSetIS()`
3156      to define a split by an arbitrary collection of entries.
3157 
3158       If no splits are set the default is used. The splits are defined by entries strided by bs,
3159       beginning at 0 then 1, etc to bs-1. The block size can be set with `PCFieldSplitSetBlockSize()`,
3160       if this is not called the block size defaults to the blocksize of the second matrix passed
3161       to `KSPSetOperators()`/`PCSetOperators()`.
3162 
3163       For the Schur complement preconditioner if
3164 
3165       ```{math}
3166       J = \left[\begin{array}{cc} A_{00} & A_{01} \\ A_{10} & A_{11} \end{array}\right]
3167       ```
3168 
3169       the preconditioner using `full` factorization is logically
3170       ```{math}
3171       \left[\begin{array}{cc} I & -\text{ksp}(A_{00}) A_{01} \\ 0 & I \end{array}\right] \left[\begin{array}{cc} \text{inv}(A_{00}) & 0 \\ 0 & \text{ksp}(S) \end{array}\right] \left[\begin{array}{cc} I & 0 \\ -A_{10} \text{ksp}(A_{00}) & I \end{array}\right]
3172       ```
3173      where the action of $\text{inv}(A_{00})$ is applied using the KSP solver with prefix `-fieldsplit_0_`.  $S$ is the Schur complement
3174      ```{math}
3175      S = A_{11} - A_{10} \text{ksp}(A_{00}) A_{01}
3176      ```
3177      which is usually dense and not stored explicitly.  The action of $\text{ksp}(S)$ is computed using the KSP solver with prefix `-fieldsplit_splitname_` (where `splitname` was given
3178      in providing the SECOND split or 1 if not given). For `PCFieldSplitGetSubKSP()` when field number is 0,
3179      it returns the KSP associated with `-fieldsplit_0_` while field number 1 gives `-fieldsplit_1_` KSP. By default
3180      $A_{11}$ is used to construct a preconditioner for $S$, use `PCFieldSplitSetSchurPre()` for all the possible ways to construct the preconditioner for $S$.
3181 
3182      The factorization type is set using `-pc_fieldsplit_schur_fact_type <diag, lower, upper, full>`. `full` is shown above,
3183      `diag` gives
3184       ```{math}
3185       \left[\begin{array}{cc} \text{inv}(A_{00}) & 0 \\  0 & -\text{ksp}(S) \end{array}\right]
3186       ```
3187      Note that, slightly counter intuitively, there is a negative in front of the $\text{ksp}(S)$  so that the preconditioner is positive definite. For SPD matrices $J$, the sign flip
3188      can be turned off with `PCFieldSplitSetSchurScale()` or by command line `-pc_fieldsplit_schur_scale 1.0`. The `lower` factorization is the inverse of
3189       ```{math}
3190       \left[\begin{array}{cc} A_{00} & 0 \\  A_{10} & S \end{array}\right]
3191       ```
3192      where the inverses of A_{00} and S are applied using KSPs. The upper factorization is the inverse of
3193       ```{math}
3194       \left[\begin{array}{cc} A_{00} & A_{01} \\  0 & S \end{array}\right]
3195       ```
3196      where again the inverses of $A_{00}$ and $S$ are applied using `KSP`s.
3197 
3198      If only one set of indices (one `IS`) is provided with `PCFieldSplitSetIS()` then the complement of that `IS`
3199      is used automatically for a second block.
3200 
3201      The fieldsplit preconditioner cannot currently be used with the `MATBAIJ` or `MATSBAIJ` data formats if the blocksize is larger than 1.
3202      Generally it should be used with the `MATAIJ` format.
3203 
3204      The forms of these preconditioners are closely related if not identical to forms derived as "Distributive Iterations", see,
3205      for example, page 294 in "Principles of Computational Fluid Dynamics" by Pieter Wesseling {cite}`wesseling2009`.
3206      One can also use `PCFIELDSPLIT`
3207      inside a smoother resulting in "Distributive Smoothers".
3208 
3209      See "A taxonomy and comparison of parallel block multi-level preconditioners for the incompressible Navier-Stokes equations" {cite}`elman2008tcp`.
3210 
3211      The Constrained Pressure Preconditioner (CPR) can be implemented using `PCCOMPOSITE` with `PCGALERKIN`. CPR first solves an $R A P$ subsystem, updates the
3212      residual on all variables (`PCCompositeSetType(pc,PC_COMPOSITE_MULTIPLICATIVE)`), and then applies a simple ILU like preconditioner on all the variables.
3213 
3214      The generalized Golub-Kahan bidiagonalization preconditioner (GKB) can be applied to symmetric $2 \times 2$ block matrices of the shape
3215      ```{math}
3216      \left[\begin{array}{cc} A_{00} & A_{01} \\ A_{01}' & 0 \end{array}\right]
3217      ```
3218      with $A_{00}$ positive semi-definite. The implementation follows {cite}`arioli2013`. Therein, we choose $N := 1/\nu * I$ and the $(1,1)$-block of the matrix is modified to $H = _{A00} + \nu*A_{01}*A_{01}'$.
3219      A linear system $Hx = b$ has to be solved in each iteration of the GKB algorithm. This solver is chosen with the option prefix `-fieldsplit_0_`.
3220 
3221    Developer Note:
3222    The Schur complement functionality of `PCFIELDSPLIT` should likely be factored into its own `PC` thus simplifying the implementation of the preconditioners and their
3223    user API.
3224 
3225 .seealso: [](sec_block_matrices), `PC`, `PCCreate()`, `PCSetType()`, `PCType`, `PC`, `PCLSC`,
3226           `PCFieldSplitGetSubKSP()`, `PCFieldSplitSchurGetSubKSP()`, `PCFieldSplitSetFields()`,
3227           `PCFieldSplitSetType()`, `PCFieldSplitSetIS()`, `PCFieldSplitSetSchurPre()`, `PCFieldSplitSetSchurFactType()`,
3228           `MatSchurComplementSetAinvType()`, `PCFieldSplitSetSchurScale()`, `PCFieldSplitSetDetectSaddlePoint()`
3229 M*/
3230 
3231 PETSC_EXTERN PetscErrorCode PCCreate_FieldSplit(PC pc)
3232 {
3233   PC_FieldSplit *jac;
3234 
3235   PetscFunctionBegin;
3236   PetscCall(PetscNew(&jac));
3237 
3238   jac->bs                 = -1;
3239   jac->nsplits            = 0;
3240   jac->type               = PC_COMPOSITE_MULTIPLICATIVE;
3241   jac->schurpre           = PC_FIELDSPLIT_SCHUR_PRE_USER; /* Try user preconditioner first, fall back on diagonal */
3242   jac->schurfactorization = PC_FIELDSPLIT_SCHUR_FACT_FULL;
3243   jac->schurscale         = -1.0;
3244   jac->dm_splits          = PETSC_TRUE;
3245   jac->detect             = PETSC_FALSE;
3246   jac->gkbtol             = 1e-5;
3247   jac->gkbdelay           = 5;
3248   jac->gkbnu              = 1;
3249   jac->gkbmaxit           = 100;
3250   jac->gkbmonitor         = PETSC_FALSE;
3251   jac->coordinates_set    = PETSC_FALSE;
3252 
3253   pc->data = (void *)jac;
3254 
3255   pc->ops->apply           = PCApply_FieldSplit;
3256   pc->ops->applytranspose  = PCApplyTranspose_FieldSplit;
3257   pc->ops->setup           = PCSetUp_FieldSplit;
3258   pc->ops->reset           = PCReset_FieldSplit;
3259   pc->ops->destroy         = PCDestroy_FieldSplit;
3260   pc->ops->setfromoptions  = PCSetFromOptions_FieldSplit;
3261   pc->ops->view            = PCView_FieldSplit;
3262   pc->ops->applyrichardson = NULL;
3263 
3264   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSchurGetSubKSP_C", PCFieldSplitSchurGetSubKSP_FieldSplit));
3265   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitGetSubKSP_C", PCFieldSplitGetSubKSP_FieldSplit));
3266   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetFields_C", PCFieldSplitSetFields_FieldSplit));
3267   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetIS_C", PCFieldSplitSetIS_FieldSplit));
3268   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetType_C", PCFieldSplitSetType_FieldSplit));
3269   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitSetBlockSize_C", PCFieldSplitSetBlockSize_FieldSplit));
3270   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCFieldSplitRestrictIS_C", PCFieldSplitRestrictIS_FieldSplit));
3271   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCSetCoordinates_C", PCSetCoordinates_FieldSplit));
3272   PetscFunctionReturn(PETSC_SUCCESS);
3273 }
3274