xref: /petsc/src/ksp/ksp/utils/schurm/schurm.c (revision 226f8a8a5081bc6ad7227cd631662400f0d6e2a0)
1 #include <../src/ksp/ksp/utils/schurm/schurm.h> /*I "petscksp.h" I*/
2 
3 const char *const MatSchurComplementAinvTypes[] = {"DIAG", "LUMP", "BLOCKDIAG", "FULL", "MatSchurComplementAinvType", "MAT_SCHUR_COMPLEMENT_AINV_", NULL};
4 
MatCreateVecs_SchurComplement(Mat N,Vec * right,Vec * left)5 PetscErrorCode MatCreateVecs_SchurComplement(Mat N, Vec *right, Vec *left)
6 {
7   Mat_SchurComplement *Na = (Mat_SchurComplement *)N->data;
8 
9   PetscFunctionBegin;
10   if (Na->D) {
11     PetscCall(MatCreateVecs(Na->D, right, left));
12     PetscFunctionReturn(PETSC_SUCCESS);
13   }
14   if (right) PetscCall(MatCreateVecs(Na->B, right, NULL));
15   if (left) PetscCall(MatCreateVecs(Na->C, NULL, left));
16   PetscFunctionReturn(PETSC_SUCCESS);
17 }
18 
MatView_SchurComplement(Mat N,PetscViewer viewer)19 PetscErrorCode MatView_SchurComplement(Mat N, PetscViewer viewer)
20 {
21   Mat_SchurComplement *Na = (Mat_SchurComplement *)N->data;
22 
23   PetscFunctionBegin;
24   PetscCall(PetscViewerASCIIPrintf(viewer, "Schur complement A11 - A10 inv(A00) A01\n"));
25   if (Na->D) {
26     PetscCall(PetscViewerASCIIPrintf(viewer, "A11\n"));
27     PetscCall(PetscViewerASCIIPushTab(viewer));
28     PetscCall(MatView(Na->D, viewer));
29     PetscCall(PetscViewerASCIIPopTab(viewer));
30   } else {
31     PetscCall(PetscViewerASCIIPrintf(viewer, "A11 = 0\n"));
32   }
33   PetscCall(PetscViewerASCIIPrintf(viewer, "A10\n"));
34   PetscCall(PetscViewerASCIIPushTab(viewer));
35   PetscCall(MatView(Na->C, viewer));
36   PetscCall(PetscViewerASCIIPopTab(viewer));
37   PetscCall(PetscViewerASCIIPrintf(viewer, "KSP solver for A00 block viewable with the additional option -%sksp_view\n", ((PetscObject)Na->ksp)->prefix ? ((PetscObject)Na->ksp)->prefix : NULL));
38   PetscCall(PetscViewerASCIIPrintf(viewer, "A01\n"));
39   PetscCall(PetscViewerASCIIPushTab(viewer));
40   PetscCall(MatView(Na->B, viewer));
41   PetscCall(PetscViewerASCIIPopTab(viewer));
42   PetscFunctionReturn(PETSC_SUCCESS);
43 }
44 
45 /*
46            A11^T - A01^T ksptrans(A00,Ap00) A10^T
47 */
MatMultTranspose_SchurComplement(Mat N,Vec x,Vec y)48 PetscErrorCode MatMultTranspose_SchurComplement(Mat N, Vec x, Vec y)
49 {
50   Mat_SchurComplement *Na = (Mat_SchurComplement *)N->data;
51 
52   PetscFunctionBegin;
53   if (!Na->work1) PetscCall(MatCreateVecs(Na->A, &Na->work1, NULL));
54   if (!Na->work2) PetscCall(MatCreateVecs(Na->A, &Na->work2, NULL));
55   PetscCall(MatMultTranspose(Na->C, x, Na->work1));
56   PetscCall(KSPSolveTranspose(Na->ksp, Na->work1, Na->work2));
57   PetscCall(MatMultTranspose(Na->B, Na->work2, y));
58   PetscCall(VecScale(y, -1.0));
59   if (Na->D) PetscCall(MatMultTransposeAdd(Na->D, x, y, y));
60   PetscFunctionReturn(PETSC_SUCCESS);
61 }
62 
63 /*
64            A11 - A10 ksp(A00,Ap00) A01
65 */
MatMult_SchurComplement(Mat N,Vec x,Vec y)66 PetscErrorCode MatMult_SchurComplement(Mat N, Vec x, Vec y)
67 {
68   Mat_SchurComplement *Na = (Mat_SchurComplement *)N->data;
69 
70   PetscFunctionBegin;
71   if (!Na->work1) PetscCall(MatCreateVecs(Na->A, &Na->work1, NULL));
72   if (!Na->work2) PetscCall(MatCreateVecs(Na->A, &Na->work2, NULL));
73   PetscCall(MatMult(Na->B, x, Na->work1));
74   PetscCall(KSPSolve(Na->ksp, Na->work1, Na->work2));
75   PetscCall(MatMult(Na->C, Na->work2, y));
76   PetscCall(VecScale(y, -1.0));
77   if (Na->D) PetscCall(MatMultAdd(Na->D, x, y, y));
78   PetscFunctionReturn(PETSC_SUCCESS);
79 }
80 
81 /*
82            A11 - A10 ksp(A00,Ap00) A01
83 */
MatMultAdd_SchurComplement(Mat N,Vec x,Vec y,Vec z)84 PetscErrorCode MatMultAdd_SchurComplement(Mat N, Vec x, Vec y, Vec z)
85 {
86   Mat_SchurComplement *Na = (Mat_SchurComplement *)N->data;
87 
88   PetscFunctionBegin;
89   if (!Na->work1) PetscCall(MatCreateVecs(Na->A, &Na->work1, NULL));
90   if (!Na->work2) PetscCall(MatCreateVecs(Na->A, &Na->work2, NULL));
91   PetscCall(MatMult(Na->B, x, Na->work1));
92   PetscCall(KSPSolve(Na->ksp, Na->work1, Na->work2));
93   if (y == z) {
94     PetscCall(VecScale(Na->work2, -1.0));
95     PetscCall(MatMultAdd(Na->C, Na->work2, z, z));
96   } else {
97     PetscCall(MatMult(Na->C, Na->work2, z));
98     PetscCall(VecAYPX(z, -1.0, y));
99   }
100   if (Na->D) PetscCall(MatMultAdd(Na->D, x, z, z));
101   PetscFunctionReturn(PETSC_SUCCESS);
102 }
103 
MatSetFromOptions_SchurComplement(Mat N,PetscOptionItems PetscOptionsObject)104 PetscErrorCode MatSetFromOptions_SchurComplement(Mat N, PetscOptionItems PetscOptionsObject)
105 {
106   Mat_SchurComplement *Na = (Mat_SchurComplement *)N->data;
107 
108   PetscFunctionBegin;
109   PetscOptionsHeadBegin(PetscOptionsObject, "MatSchurComplementOptions");
110   Na->ainvtype = MAT_SCHUR_COMPLEMENT_AINV_DIAG;
111   PetscCall(PetscOptionsEnum("-mat_schur_complement_ainv_type", "Type of approximation for DIAGFORM(A00) used when assembling Sp = A11 - A10 inv(DIAGFORM(A00)) A01", "MatSchurComplementSetAinvType", MatSchurComplementAinvTypes, (PetscEnum)Na->ainvtype,
112                              (PetscEnum *)&Na->ainvtype, NULL));
113   PetscOptionsHeadEnd();
114   PetscCall(KSPSetFromOptions(Na->ksp));
115   PetscFunctionReturn(PETSC_SUCCESS);
116 }
117 
MatDestroy_SchurComplement(Mat N)118 PetscErrorCode MatDestroy_SchurComplement(Mat N)
119 {
120   Mat_SchurComplement *Na = (Mat_SchurComplement *)N->data;
121 
122   PetscFunctionBegin;
123   PetscCall(MatDestroy(&Na->A));
124   PetscCall(MatDestroy(&Na->Ap));
125   PetscCall(MatDestroy(&Na->B));
126   PetscCall(MatDestroy(&Na->C));
127   PetscCall(MatDestroy(&Na->D));
128   PetscCall(VecDestroy(&Na->work1));
129   PetscCall(VecDestroy(&Na->work2));
130   PetscCall(KSPDestroy(&Na->ksp));
131   PetscCall(PetscFree(N->data));
132   PetscCall(PetscObjectComposeFunction((PetscObject)N, "MatProductSetFromOptions_schurcomplement_seqdense_C", NULL));
133   PetscCall(PetscObjectComposeFunction((PetscObject)N, "MatProductSetFromOptions_schurcomplement_mpidense_C", NULL));
134   PetscCall(PetscObjectComposeFunction((PetscObject)N, "MatProductSetFromOptions_anytype_C", NULL));
135   PetscFunctionReturn(PETSC_SUCCESS);
136 }
137 
138 /*@
139   MatCreateSchurComplement - Creates a new `Mat` that behaves like the Schur complement of a matrix
140 
141   Collective
142 
143   Input Parameters:
144 + A00  - the upper-left block of the original matrix $A = [A00 A01; A10 A11]$
145 . Ap00 - matrix from which the preconditioner is constructed for use in $ksp(A00,Ap00)$ to approximate the action of $A00^{-1}$
146 . A01  - the upper-right block of the original matrix $A = [A00 A01; A10 A11]$
147 . A10  - the lower-left block of the original matrix $A = [A00 A01; A10 A11]$
148 - A11  - (optional) the lower-right block of the original matrix $A = [A00 A01; A10 A11]$
149 
150   Output Parameter:
151 . S - the matrix that behaves as the Schur complement $S = A11 - A10 ksp(A00,Ap00) A01$
152 
153   Level: intermediate
154 
155   Notes:
156   The Schur complement is NOT explicitly formed! Rather, this function returns a virtual Schur complement
157   that can compute the matrix-vector product by using formula $S = A11 - A10 A^{-1} A01$
158   for Schur complement `S` and a `KSP` solver to approximate the action of $A^{-1}$.
159 
160   All four matrices must have the same MPI communicator.
161 
162   `A00` and  `A11` must be square matrices.
163 
164   `MatGetSchurComplement()` takes as arguments the index sets for the submatrices and returns both the virtual Schur complement (what this returns) plus
165   a sparse approximation to the Schur complement (useful for building a preconditioner for the Schur complement) which can be obtained from this
166   matrix with `MatSchurComplementGetPmat()`
167 
168   Developer Notes:
169   The API that includes `MatGetSchurComplement()`, `MatCreateSchurComplement()`, `MatSchurComplementGetPmat()` should be refactored to
170   remove redundancy and be clearer and simpler.
171 
172 .seealso: [](ch_ksp), `MatCreateNormal()`, `MatMult()`, `MatCreate()`, `MatSchurComplementGetKSP()`, `MatSchurComplementUpdateSubMatrices()`, `MatCreateTranspose()`, `MatGetSchurComplement()`,
173           `MatSchurComplementGetPmat()`, `MatSchurComplementSetSubMatrices()`
174 @*/
MatCreateSchurComplement(Mat A00,Mat Ap00,Mat A01,Mat A10,Mat A11,Mat * S)175 PetscErrorCode MatCreateSchurComplement(Mat A00, Mat Ap00, Mat A01, Mat A10, Mat A11, Mat *S)
176 {
177   PetscFunctionBegin;
178   PetscCall(KSPInitializePackage());
179   PetscCall(MatCreate(PetscObjectComm((PetscObject)A00), S));
180   PetscCall(MatSetType(*S, MATSCHURCOMPLEMENT));
181   PetscCall(MatSchurComplementSetSubMatrices(*S, A00, Ap00, A01, A10, A11));
182   PetscFunctionReturn(PETSC_SUCCESS);
183 }
184 
185 /*@
186   MatSchurComplementSetSubMatrices - Sets the matrices that define the Schur complement
187 
188   Collective
189 
190   Input Parameters:
191 + S    - matrix obtained with `MatSetType`(S,`MATSCHURCOMPLEMENT`)
192 . A00  - the upper-left block of the original matrix $A = [A00 A01; A10 A11]$
193 . Ap00 - matrix from which the preconditioner is constructed for use in $ksp(A00,Ap00)$ to approximate the action of $A00^{-1}$
194 . A01  - the upper-right block of the original matrix $A = [A00 A01; A10 A11]$
195 . A10  - the lower-left block of the original matrix $A = [A00 A01; A10 A11]$
196 - A11  - (optional) the lower-right block of the original matrix $A = [A00 A01; A10 A11]$
197 
198   Level: intermediate
199 
200   Notes:
201   The Schur complement is NOT explicitly formed! Rather, this
202   object performs the matrix-vector product of the Schur complement by using formula $S = A11 - A10 ksp(A00,Ap00) A01$
203 
204   All four matrices must have the same MPI communicator.
205 
206   `A00` and `A11` must be square matrices.
207 
208   This is to be used in the context of code such as
209 .vb
210      MatSetType(S,MATSCHURCOMPLEMENT);
211      MatSchurComplementSetSubMatrices(S,...);
212 .ve
213   while `MatSchurComplementUpdateSubMatrices()` should only be called after `MatCreateSchurComplement()` or `MatSchurComplementSetSubMatrices()`
214 
215 .seealso: [](ch_ksp), `Mat`, `MatCreateNormal()`, `MatMult()`, `MatCreate()`, `MatSchurComplementGetKSP()`, `MatSchurComplementUpdateSubMatrices()`, `MatCreateTranspose()`, `MatCreateSchurComplement()`, `MatGetSchurComplement()`
216 @*/
MatSchurComplementSetSubMatrices(Mat S,Mat A00,Mat Ap00,Mat A01,Mat A10,Mat A11)217 PetscErrorCode MatSchurComplementSetSubMatrices(Mat S, Mat A00, Mat Ap00, Mat A01, Mat A10, Mat A11)
218 {
219   Mat_SchurComplement *Na = (Mat_SchurComplement *)S->data;
220   PetscBool            isschur;
221 
222   PetscFunctionBegin;
223   PetscCall(PetscObjectTypeCompare((PetscObject)S, MATSCHURCOMPLEMENT, &isschur));
224   if (!isschur) PetscFunctionReturn(PETSC_SUCCESS);
225   PetscCheck(!S->assembled, PetscObjectComm((PetscObject)S), PETSC_ERR_ARG_WRONGSTATE, "Use MatSchurComplementUpdateSubMatrices() for already used matrix");
226   PetscValidHeaderSpecific(A00, MAT_CLASSID, 2);
227   PetscValidHeaderSpecific(Ap00, MAT_CLASSID, 3);
228   PetscValidHeaderSpecific(A01, MAT_CLASSID, 4);
229   PetscValidHeaderSpecific(A10, MAT_CLASSID, 5);
230   PetscCheckSameComm(A00, 2, Ap00, 3);
231   PetscCheckSameComm(A00, 2, A01, 4);
232   PetscCheckSameComm(A00, 2, A10, 5);
233   PetscCheck(A00->rmap->n == A00->cmap->n, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Local rows of A00 %" PetscInt_FMT " do not equal local columns %" PetscInt_FMT, A00->rmap->n, A00->cmap->n);
234   PetscCheck(A00->rmap->n == Ap00->rmap->n, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Local rows of A00 %" PetscInt_FMT " do not equal local rows of Ap00 %" PetscInt_FMT, A00->rmap->n, Ap00->rmap->n);
235   PetscCheck(Ap00->rmap->n == Ap00->cmap->n, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Local rows of Ap00 %" PetscInt_FMT " do not equal local columns %" PetscInt_FMT, Ap00->rmap->n, Ap00->cmap->n);
236   PetscCheck(A00->cmap->n == A01->rmap->n, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Local columns of A00 %" PetscInt_FMT " do not equal local rows of A01 %" PetscInt_FMT, A00->cmap->n, A01->rmap->n);
237   PetscCheck(A10->cmap->n == A00->rmap->n, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Local columns of A10 %" PetscInt_FMT " do not equal local rows of A00 %" PetscInt_FMT, A10->cmap->n, A00->rmap->n);
238   if (A11) {
239     PetscValidHeaderSpecific(A11, MAT_CLASSID, 6);
240     PetscCheckSameComm(A00, 2, A11, 6);
241     PetscCheck(A10->rmap->n == A11->rmap->n, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Local rows of A10 %" PetscInt_FMT " do not equal local rows A11 %" PetscInt_FMT, A10->rmap->n, A11->rmap->n);
242   }
243 
244   PetscCall(MatSetSizes(S, A10->rmap->n, A01->cmap->n, A10->rmap->N, A01->cmap->N));
245   PetscCall(PetscObjectReference((PetscObject)A00));
246   PetscCall(PetscObjectReference((PetscObject)Ap00));
247   PetscCall(PetscObjectReference((PetscObject)A01));
248   PetscCall(PetscObjectReference((PetscObject)A10));
249   PetscCall(PetscObjectReference((PetscObject)A11));
250   Na->A  = A00;
251   Na->Ap = Ap00;
252   Na->B  = A01;
253   Na->C  = A10;
254   Na->D  = A11;
255   PetscCall(MatSetUp(S));
256   PetscCall(KSPSetOperators(Na->ksp, A00, Ap00));
257   S->assembled = PETSC_TRUE;
258   PetscFunctionReturn(PETSC_SUCCESS);
259 }
260 
261 /*@
262   MatSchurComplementGetKSP - Gets the `KSP` object that is used to solve with `A00` in the Schur complement matrix $S = A11 - A10 ksp(A00,Ap00) A01$
263 
264   Not Collective
265 
266   Input Parameter:
267 . S - matrix obtained with `MatCreateSchurComplement()` (or equivalent) and implementing the action of $ A11 - A10 ksp(A00,Ap00) A01 $
268 
269   Output Parameter:
270 . ksp - the linear solver object
271 
272   Options Database Key:
273 . -fieldsplit_<splitname_0>_XXX - sets `KSP` and `PC` options for the 0-split solver inside the Schur complement used in `PCFIELDSPLIT`; default <splitname_0> is 0.
274 
275   Level: intermediate
276 
277 .seealso: [](ch_ksp), `Mat`, `MatSchurComplementSetKSP()`, `MatCreateSchurComplement()`, `MatCreateNormal()`, `MatMult()`, `MatCreate()`
278 @*/
MatSchurComplementGetKSP(Mat S,KSP * ksp)279 PetscErrorCode MatSchurComplementGetKSP(Mat S, KSP *ksp)
280 {
281   Mat_SchurComplement *Na;
282   PetscBool            isschur;
283 
284   PetscFunctionBegin;
285   PetscValidHeaderSpecific(S, MAT_CLASSID, 1);
286   PetscCall(PetscObjectTypeCompare((PetscObject)S, MATSCHURCOMPLEMENT, &isschur));
287   PetscCheck(isschur, PetscObjectComm((PetscObject)S), PETSC_ERR_ARG_WRONG, "Not for type %s", ((PetscObject)S)->type_name);
288   PetscAssertPointer(ksp, 2);
289   Na   = (Mat_SchurComplement *)S->data;
290   *ksp = Na->ksp;
291   PetscFunctionReturn(PETSC_SUCCESS);
292 }
293 
294 /*@
295   MatSchurComplementSetKSP - Sets the `KSP` object that is used to solve with `A00` in the Schur complement matrix $ S = A11 - A10 ksp(A00,Ap00) A01$
296 
297   Not Collective
298 
299   Input Parameters:
300 + S   - matrix created with `MatCreateSchurComplement()`
301 - ksp - the linear solver object
302 
303   Level: developer
304 
305   Developer Notes:
306   This is used in `PCFIELDSPLIT` to reuse the 0-split `KSP` to implement $ksp(A00,Ap00)$ in `S`.
307   The `KSP` operators are overwritten with `A00` and `Ap00` currently set in `S`.
308 
309 .seealso: [](ch_ksp), `Mat`, `MatSchurComplementGetKSP()`, `MatCreateSchurComplement()`, `MatCreateNormal()`, `MatMult()`, `MatCreate()`, `MATSCHURCOMPLEMENT`
310 @*/
MatSchurComplementSetKSP(Mat S,KSP ksp)311 PetscErrorCode MatSchurComplementSetKSP(Mat S, KSP ksp)
312 {
313   Mat_SchurComplement *Na;
314   PetscBool            isschur;
315 
316   PetscFunctionBegin;
317   PetscValidHeaderSpecific(S, MAT_CLASSID, 1);
318   PetscCall(PetscObjectTypeCompare((PetscObject)S, MATSCHURCOMPLEMENT, &isschur));
319   if (!isschur) PetscFunctionReturn(PETSC_SUCCESS);
320   PetscValidHeaderSpecific(ksp, KSP_CLASSID, 2);
321   Na = (Mat_SchurComplement *)S->data;
322   PetscCall(PetscObjectReference((PetscObject)ksp));
323   PetscCall(KSPDestroy(&Na->ksp));
324   Na->ksp = ksp;
325   PetscCall(KSPSetOperators(Na->ksp, Na->A, Na->Ap));
326   PetscFunctionReturn(PETSC_SUCCESS);
327 }
328 
329 /*@
330   MatSchurComplementUpdateSubMatrices - Updates the Schur complement matrix object with new submatrices
331 
332   Collective
333 
334   Input Parameters:
335 + S    - matrix obtained with `MatCreateSchurComplement()` (or `MatSchurSetSubMatrices()`) and implementing the action of $A11 - A10 ksp(A00,Ap00) A01$
336 . A00  - the upper-left block of the original matrix $A = [A00 A01; A10 A11]$
337 . Ap00 - matrix from which the preconditioner is constructed for use in $ksp(A00,Ap00)$ to approximate the action of $A00^{-1}$
338 . A01  - the upper-right block of the original matrix $A = [A00 A01; A10 A11]$
339 . A10  - the lower-left block of the original matrix $A = [A00 A01; A10 A11]$
340 - A11  - (optional) the lower-right block of the original matrix $A = [A00 A01; A10 A11]$
341 
342   Level: intermediate
343 
344   Notes:
345   All four matrices must have the same MPI communicator
346 
347   `A00` and  `A11` must be square matrices
348 
349   All of the matrices provided must have the same sizes as was used with `MatCreateSchurComplement()` or `MatSchurComplementSetSubMatrices()`
350   though they need not be the same matrices.
351 
352   This can only be called after `MatCreateSchurComplement()` or `MatSchurComplementSetSubMatrices()`, it cannot be called immediately after `MatSetType`(S,`MATSCHURCOMPLEMENT`);
353 
354   Developer Notes:
355   This code is almost identical to `MatSchurComplementSetSubMatrices()`. The API should be refactored.
356 
357 .seealso: [](ch_ksp), `Mat`, `MatCreateNormal()`, `MatMult()`, `MatCreate()`, `MatSchurComplementGetKSP()`, `MatCreateSchurComplement()`
358 @*/
MatSchurComplementUpdateSubMatrices(Mat S,Mat A00,Mat Ap00,Mat A01,Mat A10,Mat A11)359 PetscErrorCode MatSchurComplementUpdateSubMatrices(Mat S, Mat A00, Mat Ap00, Mat A01, Mat A10, Mat A11)
360 {
361   Mat_SchurComplement *Na = (Mat_SchurComplement *)S->data;
362   PetscBool            isschur;
363 
364   PetscFunctionBegin;
365   PetscValidHeaderSpecific(S, MAT_CLASSID, 1);
366   PetscCall(PetscObjectTypeCompare((PetscObject)S, MATSCHURCOMPLEMENT, &isschur));
367   if (!isschur) PetscFunctionReturn(PETSC_SUCCESS);
368   PetscCheck(S->assembled, PetscObjectComm((PetscObject)S), PETSC_ERR_ARG_WRONGSTATE, "Use MatSchurComplementSetSubMatrices() for a new matrix");
369   PetscValidHeaderSpecific(A00, MAT_CLASSID, 2);
370   PetscValidHeaderSpecific(Ap00, MAT_CLASSID, 3);
371   PetscValidHeaderSpecific(A01, MAT_CLASSID, 4);
372   PetscValidHeaderSpecific(A10, MAT_CLASSID, 5);
373   PetscCheckSameComm(A00, 2, Ap00, 3);
374   PetscCheckSameComm(A00, 2, A01, 4);
375   PetscCheckSameComm(A00, 2, A10, 5);
376   PetscCheck(A00->rmap->n == A00->cmap->n, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Local rows of A00 %" PetscInt_FMT " do not equal local columns %" PetscInt_FMT, A00->rmap->n, A00->cmap->n);
377   PetscCheck(A00->rmap->n == Ap00->rmap->n, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Local rows of A00 %" PetscInt_FMT " do not equal local rows of Ap00 %" PetscInt_FMT, A00->rmap->n, Ap00->rmap->n);
378   PetscCheck(Ap00->rmap->n == Ap00->cmap->n, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Local rows of Ap00 %" PetscInt_FMT " do not equal local columns %" PetscInt_FMT, Ap00->rmap->n, Ap00->cmap->n);
379   PetscCheck(A00->cmap->n == A01->rmap->n, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Local columns of A00 %" PetscInt_FMT " do not equal local rows of A01 %" PetscInt_FMT, A00->cmap->n, A01->rmap->n);
380   PetscCheck(A10->cmap->n == A00->rmap->n, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Local columns of A10 %" PetscInt_FMT " do not equal local rows of A00 %" PetscInt_FMT, A10->cmap->n, A00->rmap->n);
381   if (A11) {
382     PetscValidHeaderSpecific(A11, MAT_CLASSID, 6);
383     PetscCheckSameComm(A00, 2, A11, 6);
384     PetscCheck(A10->rmap->n == A11->rmap->n, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Local rows of A10 %" PetscInt_FMT " do not equal local rows A11 %" PetscInt_FMT, A10->rmap->n, A11->rmap->n);
385   }
386 
387   PetscCall(PetscObjectReference((PetscObject)A00));
388   PetscCall(PetscObjectReference((PetscObject)Ap00));
389   PetscCall(PetscObjectReference((PetscObject)A01));
390   PetscCall(PetscObjectReference((PetscObject)A10));
391   if (A11) PetscCall(PetscObjectReference((PetscObject)A11));
392 
393   PetscCall(MatDestroy(&Na->A));
394   PetscCall(MatDestroy(&Na->Ap));
395   PetscCall(MatDestroy(&Na->B));
396   PetscCall(MatDestroy(&Na->C));
397   PetscCall(MatDestroy(&Na->D));
398 
399   Na->A  = A00;
400   Na->Ap = Ap00;
401   Na->B  = A01;
402   Na->C  = A10;
403   Na->D  = A11;
404 
405   PetscCall(KSPSetOperators(Na->ksp, A00, Ap00));
406   PetscFunctionReturn(PETSC_SUCCESS);
407 }
408 
409 /*@
410   MatSchurComplementGetSubMatrices - Get the individual submatrices in the Schur complement
411 
412   Collective
413 
414   Input Parameter:
415 . S - matrix obtained with `MatCreateSchurComplement()` (or equivalent) and implementing the action of $A11 - A10 ksp(A00,Ap00) A01$
416 
417   Output Parameters:
418 + A00  - the upper-left block of the original matrix $A = [A00 A01; A10 A11]$
419 . Ap00 - matrix from which the preconditioner is constructed for use in $ksp(A00,Ap00)$ to approximate the action of $A^{-1}$
420 . A01  - the upper-right block of the original matrix $A = [A00 A01; A10 A11]$
421 . A10  - the lower-left block of the original matrix $A = [A00 A01; A10 A11]$
422 - A11  - (optional) the lower-right block of the original matrix $A = [A00 A01; A10 A11]$
423 
424   Level: intermediate
425 
426   Note:
427   Use `NULL` for any unneeded output argument.
428 
429   The reference counts of the submatrices are not increased before they are returned and the matrices should not be modified or destroyed.
430 
431 .seealso: [](ch_ksp), `MatCreateNormal()`, `MatMult()`, `MatCreate()`, `MatSchurComplementGetKSP()`, `MatCreateSchurComplement()`, `MatSchurComplementUpdateSubMatrices()`
432 @*/
MatSchurComplementGetSubMatrices(Mat S,Mat * A00,Mat * Ap00,Mat * A01,Mat * A10,Mat * A11)433 PetscErrorCode MatSchurComplementGetSubMatrices(Mat S, Mat *A00, Mat *Ap00, Mat *A01, Mat *A10, Mat *A11)
434 {
435   Mat_SchurComplement *Na = (Mat_SchurComplement *)S->data;
436   PetscBool            flg;
437 
438   PetscFunctionBegin;
439   PetscValidHeaderSpecific(S, MAT_CLASSID, 1);
440   PetscCall(PetscObjectTypeCompare((PetscObject)S, MATSCHURCOMPLEMENT, &flg));
441   PetscCheck(flg, PetscObjectComm((PetscObject)S), PETSC_ERR_ARG_WRONG, "Not for type %s", ((PetscObject)S)->type_name);
442   if (A00) *A00 = Na->A;
443   if (Ap00) *Ap00 = Na->Ap;
444   if (A01) *A01 = Na->B;
445   if (A10) *A10 = Na->C;
446   if (A11) *A11 = Na->D;
447   PetscFunctionReturn(PETSC_SUCCESS);
448 }
449 
450 #include <petsc/private/kspimpl.h>
451 
452 /*@
453   MatSchurComplementComputeExplicitOperator - Compute the Schur complement matrix explicitly
454 
455   Collective
456 
457   Input Parameter:
458 . A - the matrix obtained with `MatCreateSchurComplement()`
459 
460   Output Parameter:
461 . S - the Schur complement matrix
462 
463   Level: advanced
464 
465   Notes:
466   This can be expensive when `S` is large, so it is mainly for testing
467 
468   Use `MatSchurComplementGetPmat()` to get a sparse approximation for the Schur complement suitable for use in building a preconditioner
469 
470   `S` will automatically have the same prefix as `A` appended by `explicit_operator_`,
471   there are three options available: `-fieldsplit_1_explicit_operator_mat_type`,
472   `-fieldsplit_1_explicit_operator_mat_symmetric`, and `-fieldsplit_1_explicit_operator_mat_hermitian`
473 
474   Developer Note:
475   The three aforementioned should not be parsed and used in this routine, but rather in `MatSetFromOptions()`
476 
477 .seealso: [](ch_ksp), `MatCreateSchurComplement()`, `MatSchurComplementUpdateSubMatrices()`, `MatSchurComplementGetPmat()`
478 @*/
MatSchurComplementComputeExplicitOperator(Mat A,Mat * S)479 PetscErrorCode MatSchurComplementComputeExplicitOperator(Mat A, Mat *S)
480 {
481   Mat       P, B, C, D, E = NULL, Bd, AinvBd, sub = NULL;
482   MatType   mtype;
483   VecType   vtype;
484   KSP       ksp;
485   PetscInt  n, N, m, M;
486   PetscBool flg = PETSC_FALSE, set, symm;
487   char      prefix[256], type[256];
488 
489   PetscFunctionBegin;
490   PetscAssertPointer(S, 2);
491   if (*S) PetscValidHeaderSpecific(*S, MAT_CLASSID, 2);
492   PetscCall(PetscObjectQuery((PetscObject)A, "AinvB", (PetscObject *)&AinvBd));
493   set = (PetscBool)(AinvBd != NULL);
494   if (set && AinvBd->cmap->N == -1) PetscFunctionReturn(PETSC_SUCCESS); // early bail out if composed Mat is uninitialized
495   PetscCall(MatSchurComplementGetSubMatrices(A, &P, NULL, &B, &C, &D));
496   PetscCall(MatGetVecType(B, &vtype));
497   PetscCall(MatGetLocalSize(B, &m, &n));
498   PetscCall(MatSchurComplementGetKSP(A, &ksp));
499   PetscCall(KSPSetUp(ksp));
500   if (set) {
501     PetscCheck(AinvBd->cmap->N >= A->cmap->N, PetscObjectComm((PetscObject)A), PETSC_ERR_ARG_SIZ, "Composed Mat should have at least as many columns as the Schur complement (%" PetscInt_FMT " >= %" PetscInt_FMT ")", AinvBd->cmap->N, A->cmap->N);
502     PetscCall(MatGetType(AinvBd, &mtype));
503     if (AinvBd->cmap->N > A->cmap->N) {
504       Mat s[2];
505 
506       PetscCall(MatDuplicate(AinvBd, MAT_DO_NOT_COPY_VALUES, &Bd));
507       PetscCall(MatDenseGetSubMatrix(Bd, PETSC_DECIDE, PETSC_DECIDE, A->cmap->N, AinvBd->cmap->N, s));
508       PetscCall(MatDenseGetSubMatrix(AinvBd, PETSC_DECIDE, PETSC_DECIDE, A->cmap->N, AinvBd->cmap->N, s + 1));
509       PetscCall(MatCopy(s[1], s[0], SAME_NONZERO_PATTERN)); // copy the last columns of the composed Mat, which are likely the input columns of PCApply_FieldSplit_Schur()
510       PetscCall(MatDenseRestoreSubMatrix(AinvBd, s + 1));
511       PetscCall(MatDenseRestoreSubMatrix(Bd, s));
512       PetscCall(MatDenseGetSubMatrix(Bd, PETSC_DECIDE, PETSC_DECIDE, 0, A->cmap->N, &sub));
513       PetscCall(MatConvert(B, mtype, MAT_REUSE_MATRIX, &sub)); // copy A01 into the first columns of the block of RHS of KSPMatSolve()
514       PetscCall(MatDenseRestoreSubMatrix(Bd, &sub));
515     } else PetscCall(MatConvert(B, mtype, MAT_INITIAL_MATRIX, &Bd));
516   } else {
517     PetscCall(MatGetSize(B, &M, &N));
518     PetscCall(MatCreateDenseFromVecType(PetscObjectComm((PetscObject)A), vtype, m, n, M, N, -1, NULL, &AinvBd));
519     PetscCall(MatGetType(AinvBd, &mtype));
520     PetscCall(MatConvert(B, mtype, MAT_INITIAL_MATRIX, &Bd));
521   }
522   PetscCall(KSPMatSolve(ksp, Bd, AinvBd));
523   if (set && AinvBd->cmap->N > A->cmap->N) {
524     Mat          AinvB;
525     PetscScalar *v;
526     PetscBool    match;
527 
528     PetscCall(PetscObjectTypeCompareAny((PetscObject)AinvBd, &match, MATSEQDENSECUDA, MATMPIDENSECUDA, ""));
529     if (match) {
530 #if PetscDefined(HAVE_CUDA)
531       PetscCall(MatDenseCUDAGetArrayWrite(AinvBd, &v));
532       PetscCall(MatCreateDenseCUDA(PetscObjectComm((PetscObject)A), AinvBd->rmap->n, A->cmap->n, AinvBd->rmap->N, A->cmap->N, v, &AinvB));
533       PetscCall(MatDenseCUDAReplaceArray(AinvB, v));
534       PetscCall(MatDenseCUDARestoreArrayWrite(AinvBd, &v));
535 #endif
536     } else {
537       PetscCall(PetscObjectTypeCompareAny((PetscObject)AinvBd, &match, MATSEQDENSEHIP, MATMPIDENSEHIP, ""));
538       if (match) {
539 #if PetscDefined(HAVE_HIP)
540         PetscCall(MatDenseHIPGetArrayWrite(AinvBd, &v));
541         PetscCall(MatCreateDenseHIP(PetscObjectComm((PetscObject)A), AinvBd->rmap->n, A->cmap->n, AinvBd->rmap->N, A->cmap->N, v, &AinvB));
542         PetscCall(MatDenseHIPReplaceArray(AinvB, v));
543         PetscCall(MatDenseHIPRestoreArrayWrite(AinvBd, &v));
544 #endif
545       } else {
546         PetscCall(MatDenseGetArrayWrite(AinvBd, &v)); // no easy way to resize a Mat, so create a new one with the same data pointer
547         PetscCall(MatCreateDense(PetscObjectComm((PetscObject)A), AinvBd->rmap->n, A->cmap->n, AinvBd->rmap->N, A->cmap->N, v, &AinvB));
548         PetscCall(MatDenseReplaceArray(AinvB, v)); // let MatDestroy() free the data pointer
549         PetscCall(MatDenseRestoreArrayWrite(AinvBd, &v));
550       }
551     }
552     PetscCall(MatHeaderReplace(AinvBd, &AinvB)); // replace the input composed Mat with just A00^-1 A01 (trailing columns are removed)
553   }
554   PetscCall(MatDestroy(&Bd));
555   if (!set) PetscCall(MatFilter(AinvBd, PETSC_SMALL, PETSC_FALSE, PETSC_FALSE));
556   if (D && !*S) {
557     PetscCall(MatGetLocalSize(D, &m, &n));
558     PetscCall(MatGetSize(D, &M, &N));
559     PetscCall(MatCreateDenseFromVecType(PetscObjectComm((PetscObject)A), vtype, m, n, M, N, -1, NULL, S));
560   } else if (*S) {
561     PetscCall(MatGetType(AinvBd, &mtype));
562     PetscCall(MatSetType(*S, mtype));
563   }
564   PetscCall(MatMatMult(C, AinvBd, *S ? MAT_REUSE_MATRIX : MAT_INITIAL_MATRIX, PETSC_DETERMINE, S));
565   if (!set) PetscCall(MatDestroy(&AinvBd));
566   else {
567     PetscCall(MatScale(AinvBd, -1.0));
568     PetscCall(MatFilter(AinvBd, PETSC_MACHINE_EPSILON, PETSC_FALSE, PETSC_FALSE));
569     PetscCall(MatFilter(*S, PETSC_MACHINE_EPSILON, PETSC_FALSE, PETSC_FALSE));
570   }
571   if (D) {
572     PetscCall(PetscObjectTypeCompareAny((PetscObject)D, &flg, MATSEQSBAIJ, MATMPISBAIJ, ""));
573     if (flg) {
574       PetscCall(MatIsSymmetricKnown(A, &set, &symm));
575       if (!set || !symm) PetscCall(MatConvert(D, MATBAIJ, MAT_INITIAL_MATRIX, &E)); /* convert the (1,1) block to nonsymmetric storage for MatAXPY() */
576     }
577     PetscCall(MatAXPY(*S, -1.0, E ? E : D, DIFFERENT_NONZERO_PATTERN));        /* calls Mat[Get|Restore]RowUpperTriangular(), so only the upper triangular part is valid with symmetric storage */
578     if (!E && flg) PetscCall(MatConvert(*S, MATSBAIJ, MAT_INPLACE_MATRIX, S)); /* if A is symmetric and the (1,1) block is a MatSBAIJ, return S as a MatSBAIJ since the lower triangular part is invalid */
579   }
580   PetscCall(MatDestroy(&E));
581   PetscCall(MatScale(*S, -1.0));
582   PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "%sexplicit_operator_", ((PetscObject)A)->prefix ? ((PetscObject)A)->prefix : ""));
583   PetscCall(MatSetOptionsPrefix(*S, prefix));
584   PetscObjectOptionsBegin((PetscObject)*S);
585   PetscCall(PetscOptionsFList("-mat_type", "Matrix type", "MatSetType", MatList, !E && flg ? MATSBAIJ : mtype, type, 256, &set));
586   if (set) PetscCall(MatConvert(*S, type, MAT_INPLACE_MATRIX, S));
587   flg = PETSC_FALSE;
588   PetscCall(PetscOptionsBool("-mat_symmetric", "Sets the MAT_SYMMETRIC option", "MatSetOption", flg, &flg, &set));
589   if (set) PetscCall(MatSetOption(*S, MAT_SYMMETRIC, flg));
590   if (PetscDefined(USE_COMPLEX)) {
591     flg = PETSC_FALSE;
592     PetscCall(PetscOptionsBool("-mat_hermitian", "Sets the MAT_HERMITIAN option", "MatSetOption", flg, &flg, &set));
593     if (set) PetscCall(MatSetOption(*S, MAT_HERMITIAN, flg));
594   }
595   PetscOptionsEnd();
596   PetscFunctionReturn(PETSC_SUCCESS);
597 }
598 
599 /* Developer Notes:
600     This should be implemented with a MatCreate_SchurComplement() as that is the standard design for new Mat classes. */
MatGetSchurComplement_Basic(Mat mat,IS isrow0,IS iscol0,IS isrow1,IS iscol1,MatReuse mreuse,Mat * S,MatSchurComplementAinvType ainvtype,MatReuse preuse,Mat * Sp)601 PetscErrorCode MatGetSchurComplement_Basic(Mat mat, IS isrow0, IS iscol0, IS isrow1, IS iscol1, MatReuse mreuse, Mat *S, MatSchurComplementAinvType ainvtype, MatReuse preuse, Mat *Sp)
602 {
603   Mat      A = NULL, Ap = NULL, B = NULL, C = NULL, D = NULL;
604   MatReuse reuse;
605 
606   PetscFunctionBegin;
607   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
608   PetscValidType(mat, 1);
609   PetscValidHeaderSpecific(isrow0, IS_CLASSID, 2);
610   PetscValidHeaderSpecific(iscol0, IS_CLASSID, 3);
611   PetscValidHeaderSpecific(isrow1, IS_CLASSID, 4);
612   PetscValidHeaderSpecific(iscol1, IS_CLASSID, 5);
613   PetscValidLogicalCollectiveEnum(mat, mreuse, 6);
614   PetscValidLogicalCollectiveEnum(mat, ainvtype, 8);
615   PetscValidLogicalCollectiveEnum(mat, preuse, 9);
616   if (mreuse == MAT_IGNORE_MATRIX && preuse == MAT_IGNORE_MATRIX) PetscFunctionReturn(PETSC_SUCCESS);
617   if (mreuse == MAT_REUSE_MATRIX) PetscValidHeaderSpecific(*S, MAT_CLASSID, 7);
618   if (preuse == MAT_REUSE_MATRIX) PetscValidHeaderSpecific(*Sp, MAT_CLASSID, 10);
619 
620   PetscCheck(!mat->factortype, PetscObjectComm((PetscObject)mat), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix");
621 
622   reuse = MAT_INITIAL_MATRIX;
623   if (mreuse == MAT_REUSE_MATRIX) {
624     PetscCall(MatSchurComplementGetSubMatrices(*S, &A, &Ap, &B, &C, &D));
625     PetscCheck(A && Ap && B && C, PetscObjectComm((PetscObject)mat), PETSC_ERR_ARG_WRONGSTATE, "Attempting to reuse matrix but Schur complement matrices unset");
626     PetscCheck(A == Ap, PetscObjectComm((PetscObject)mat), PETSC_ERR_ARG_WRONGSTATE, "Matrix for constructing the preconditioner does not match operator");
627     PetscCall(MatDestroy(&Ap)); /* get rid of extra reference */
628     reuse = MAT_REUSE_MATRIX;
629   }
630   PetscCall(MatCreateSubMatrix(mat, isrow0, iscol0, reuse, &A));
631   PetscCall(MatCreateSubMatrix(mat, isrow0, iscol1, reuse, &B));
632   PetscCall(MatCreateSubMatrix(mat, isrow1, iscol0, reuse, &C));
633   PetscCall(MatCreateSubMatrix(mat, isrow1, iscol1, reuse, &D));
634   switch (mreuse) {
635   case MAT_INITIAL_MATRIX:
636     PetscCall(MatCreateSchurComplement(A, A, B, C, D, S));
637     break;
638   case MAT_REUSE_MATRIX:
639     PetscCall(MatSchurComplementUpdateSubMatrices(*S, A, A, B, C, D));
640     break;
641   default:
642     PetscCheck(mreuse == MAT_IGNORE_MATRIX, PetscObjectComm((PetscObject)mat), PETSC_ERR_SUP, "Unrecognized value of mreuse %d", (int)mreuse);
643   }
644   if (preuse != MAT_IGNORE_MATRIX) PetscCall(MatCreateSchurComplementPmat(A, B, C, D, ainvtype, preuse, Sp));
645   PetscCall(MatDestroy(&A));
646   PetscCall(MatDestroy(&B));
647   PetscCall(MatDestroy(&C));
648   PetscCall(MatDestroy(&D));
649   PetscFunctionReturn(PETSC_SUCCESS);
650 }
651 
652 /*@
653   MatGetSchurComplement - Obtain the Schur complement from eliminating part of the matrix in another part.
654 
655   Collective
656 
657   Input Parameters:
658 + A        - matrix in which the complement is to be taken
659 . isrow0   - rows to eliminate
660 . iscol0   - columns to eliminate, (isrow0,iscol0) should be square and nonsingular
661 . isrow1   - rows in which the Schur complement is formed
662 . iscol1   - columns in which the Schur complement is formed
663 . mreuse   - `MAT_INITIAL_MATRIX` or `MAT_REUSE_MATRIX`, use `MAT_IGNORE_MATRIX` to put nothing in `S`
664 . ainvtype - the type of approximation used for the inverse of the (0,0) block used in forming `Sp`:
665              `MAT_SCHUR_COMPLEMENT_AINV_DIAG`, `MAT_SCHUR_COMPLEMENT_AINV_LUMP`, `MAT_SCHUR_COMPLEMENT_AINV_BLOCK_DIAG`, or `MAT_SCHUR_COMPLEMENT_AINV_FULL`
666 - preuse   - `MAT_INITIAL_MATRIX` or `MAT_REUSE_MATRIX`, use `MAT_IGNORE_MATRIX` to put nothing in `Sp`
667 
668   Output Parameters:
669 + S  - exact Schur complement, often of type `MATSCHURCOMPLEMENT` which is difficult to use for preconditioning
670 - Sp - approximate Schur complement from which a preconditioner can be built $A11 - A10 inv(DIAGFORM(A00)) A01$
671 
672   Level: advanced
673 
674   Notes:
675   Since the real Schur complement is usually dense, providing a good approximation to `Sp` usually requires
676   application-specific information.
677 
678   Sometimes users would like to provide problem-specific data in the Schur complement, usually only for special row
679   and column index sets.  In that case, the user should call `PetscObjectComposeFunction()` on the *S matrix and pass mreuse of `MAT_REUSE_MATRIX` to set
680   "MatGetSchurComplement_C" to their function.  If their function needs to fall back to the default implementation, it
681   should call `MatGetSchurComplement_Basic()`.
682 
683   `MatCreateSchurComplement()` takes as arguments the four submatrices and returns the virtual Schur complement (what this function returns in S).
684 
685   `MatSchurComplementGetPmat()` takes the virtual Schur complement and returns an explicit approximate Schur complement (what this returns in Sp).
686 
687   In other words calling `MatCreateSchurComplement()` followed by `MatSchurComplementGetPmat()` produces the same output as this function but with slightly different
688   inputs. The actually submatrices of the original block matrix instead of index sets to the submatrices.
689 
690   Developer Notes:
691   The API that includes `MatGetSchurComplement()`, `MatCreateSchurComplement()`, `MatSchurComplementGetPmat()` should be refactored to
692   remove redundancy and be clearer and simpler.
693 
694 .seealso: [](ch_ksp), `MatCreateSubMatrix()`, `PCFIELDSPLIT`, `MatCreateSchurComplement()`, `MatSchurComplementAinvType`
695 @*/
MatGetSchurComplement(Mat A,IS isrow0,IS iscol0,IS isrow1,IS iscol1,MatReuse mreuse,Mat * S,MatSchurComplementAinvType ainvtype,MatReuse preuse,Mat * Sp)696 PetscErrorCode MatGetSchurComplement(Mat A, IS isrow0, IS iscol0, IS isrow1, IS iscol1, MatReuse mreuse, Mat *S, MatSchurComplementAinvType ainvtype, MatReuse preuse, Mat *Sp)
697 {
698   PetscErrorCode (*f)(Mat, IS, IS, IS, IS, MatReuse, Mat *, MatReuse, Mat *) = NULL;
699 
700   PetscFunctionBegin;
701   PetscValidHeaderSpecific(A, MAT_CLASSID, 1);
702   PetscValidHeaderSpecific(isrow0, IS_CLASSID, 2);
703   PetscValidHeaderSpecific(iscol0, IS_CLASSID, 3);
704   PetscValidHeaderSpecific(isrow1, IS_CLASSID, 4);
705   PetscValidHeaderSpecific(iscol1, IS_CLASSID, 5);
706   PetscValidLogicalCollectiveEnum(A, mreuse, 6);
707   if (mreuse == MAT_REUSE_MATRIX) PetscValidHeaderSpecific(*S, MAT_CLASSID, 7);
708   PetscValidLogicalCollectiveEnum(A, ainvtype, 8);
709   PetscValidLogicalCollectiveEnum(A, preuse, 9);
710   if (preuse == MAT_REUSE_MATRIX) PetscValidHeaderSpecific(*Sp, MAT_CLASSID, 10);
711   PetscValidType(A, 1);
712   PetscCheck(!A->factortype, PetscObjectComm((PetscObject)A), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix");
713   if (mreuse == MAT_REUSE_MATRIX) { /* This is the only situation, in which we can demand that the user pass a non-NULL pointer to non-garbage in S. */
714     PetscCall(PetscObjectQueryFunction((PetscObject)*S, "MatGetSchurComplement_C", &f));
715   }
716   if (f) PetscCall((*f)(A, isrow0, iscol0, isrow1, iscol1, mreuse, S, preuse, Sp));
717   else PetscCall(MatGetSchurComplement_Basic(A, isrow0, iscol0, isrow1, iscol1, mreuse, S, ainvtype, preuse, Sp));
718   PetscFunctionReturn(PETSC_SUCCESS);
719 }
720 
721 /*@
722   MatSchurComplementSetAinvType - set the type of approximation used for the inverse of the (0,0) block used in forming `Sp` in `MatSchurComplementGetPmat()`
723 
724   Not Collective
725 
726   Input Parameters:
727 + S        - matrix obtained with `MatCreateSchurComplement()` (or equivalent) and implementing the action of $A11 - A10 ksp(A00,Ap00) A01$
728 - ainvtype - type of approximation to be used to form approximate Schur complement $Sp = A11 - A10 inv(DIAGFORM(A00)) A01$:
729              `MAT_SCHUR_COMPLEMENT_AINV_DIAG`, `MAT_SCHUR_COMPLEMENT_AINV_LUMP`, `MAT_SCHUR_COMPLEMENT_AINV_BLOCK_DIAG`, or `MAT_SCHUR_COMPLEMENT_AINV_FULL`
730 
731   Options Database Key:
732 . -mat_schur_complement_ainv_type diag | lump | blockdiag | full - set schur complement type
733 
734   Level: advanced
735 
736 .seealso: [](ch_ksp), `MatSchurComplementAinvType`, `MatCreateSchurComplement()`, `MatGetSchurComplement()`, `MatSchurComplementGetPmat()`, `MatSchurComplementGetAinvType()`
737 @*/
MatSchurComplementSetAinvType(Mat S,MatSchurComplementAinvType ainvtype)738 PetscErrorCode MatSchurComplementSetAinvType(Mat S, MatSchurComplementAinvType ainvtype)
739 {
740   PetscBool            isschur;
741   Mat_SchurComplement *schur;
742 
743   PetscFunctionBegin;
744   PetscValidHeaderSpecific(S, MAT_CLASSID, 1);
745   PetscCall(PetscObjectTypeCompare((PetscObject)S, MATSCHURCOMPLEMENT, &isschur));
746   if (!isschur) PetscFunctionReturn(PETSC_SUCCESS);
747   PetscValidLogicalCollectiveEnum(S, ainvtype, 2);
748   schur = (Mat_SchurComplement *)S->data;
749   PetscCheck(ainvtype == MAT_SCHUR_COMPLEMENT_AINV_DIAG || ainvtype == MAT_SCHUR_COMPLEMENT_AINV_LUMP || ainvtype == MAT_SCHUR_COMPLEMENT_AINV_BLOCK_DIAG || ainvtype == MAT_SCHUR_COMPLEMENT_AINV_FULL, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unknown MatSchurComplementAinvType: %d", (int)ainvtype);
750   schur->ainvtype = ainvtype;
751   PetscFunctionReturn(PETSC_SUCCESS);
752 }
753 
754 /*@
755   MatSchurComplementGetAinvType - get the type of approximation for the inverse of the (0,0) block used in forming `Sp` in `MatSchurComplementGetPmat()`
756 
757   Not Collective
758 
759   Input Parameter:
760 . S - matrix obtained with `MatCreateSchurComplement()` (or equivalent) and implementing the action of $A11 - A10 ksp(A00,Ap00) A01$
761 
762   Output Parameter:
763 . ainvtype - type of approximation used to form approximate Schur complement Sp = A11 - A10 inv(DIAGFORM(A00)) A01:
764              `MAT_SCHUR_COMPLEMENT_AINV_DIAG`, `MAT_SCHUR_COMPLEMENT_AINV_LUMP`, `MAT_SCHUR_COMPLEMENT_AINV_BLOCK_DIAG`, or `MAT_SCHUR_COMPLEMENT_AINV_FULL`
765 
766   Level: advanced
767 
768 .seealso: [](ch_ksp), `MatSchurComplementAinvType`, `MatCreateSchurComplement()`, `MatGetSchurComplement()`, `MatSchurComplementGetPmat()`, `MatSchurComplementSetAinvType()`
769 @*/
MatSchurComplementGetAinvType(Mat S,MatSchurComplementAinvType * ainvtype)770 PetscErrorCode MatSchurComplementGetAinvType(Mat S, MatSchurComplementAinvType *ainvtype)
771 {
772   PetscBool            isschur;
773   Mat_SchurComplement *schur;
774 
775   PetscFunctionBegin;
776   PetscValidHeaderSpecific(S, MAT_CLASSID, 1);
777   PetscCall(PetscObjectTypeCompare((PetscObject)S, MATSCHURCOMPLEMENT, &isschur));
778   PetscCheck(isschur, PetscObjectComm((PetscObject)S), PETSC_ERR_ARG_WRONG, "Not for type %s", ((PetscObject)S)->type_name);
779   schur = (Mat_SchurComplement *)S->data;
780   if (ainvtype) *ainvtype = schur->ainvtype;
781   PetscFunctionReturn(PETSC_SUCCESS);
782 }
783 
784 /*@
785   MatCreateSchurComplementPmat - create a matrix for preconditioning the Schur complement by explicitly assembling the sparse matrix
786   $Sp = A11 - A10 inv(DIAGFORM(A00)) A01$
787 
788   Collective
789 
790   Input Parameters:
791 + A00      - the upper-left part of the original matrix $A = [A00 A01; A10 A11]$
792 . A01      - (optional) the upper-right part of the original matrix $A = [A00 A01; A10 A11]$
793 . A10      - (optional) the lower-left part of the original matrix $A = [A00 A01; A10 A11]$
794 . A11      - (optional) the lower-right part of the original matrix $A = [A00 A01; A10 A11]$
795 . ainvtype - type of approximation for DIAGFORM(A00) used when forming $Sp = A11 - A10 inv(DIAGFORM(A00)) A01$. See `MatSchurComplementAinvType`.
796 - preuse   - `MAT_INITIAL_MATRIX` for a new `Sp`, or `MAT_REUSE_MATRIX` to reuse an existing `Sp`, or `MAT_IGNORE_MATRIX` to put nothing in `Sp`
797 
798   Output Parameter:
799 . Sp - approximate Schur complement suitable for constructing a preconditioner for the true Schur complement $S = A11 - A10 inv(A00) A01$
800 
801   Level: advanced
802 
803 .seealso: [](ch_ksp), `MatCreateSchurComplement()`, `MatGetSchurComplement()`, `MatSchurComplementGetPmat()`, `MatSchurComplementAinvType`
804 @*/
MatCreateSchurComplementPmat(Mat A00,Mat A01,Mat A10,Mat A11,MatSchurComplementAinvType ainvtype,MatReuse preuse,Mat * Sp)805 PetscErrorCode MatCreateSchurComplementPmat(Mat A00, Mat A01, Mat A10, Mat A11, MatSchurComplementAinvType ainvtype, MatReuse preuse, Mat *Sp)
806 {
807   PetscInt N00;
808 
809   PetscFunctionBegin;
810   /* Use an appropriate approximate inverse of A00 to form A11 - A10 inv(DIAGFORM(A00)) A01; a NULL A01, A10 or A11 indicates a zero matrix. */
811   /* TODO: Perhaps should create an appropriately-sized zero matrix of the same type as A00? */
812   PetscValidLogicalCollectiveEnum(A11, preuse, 6);
813   if (preuse == MAT_IGNORE_MATRIX) PetscFunctionReturn(PETSC_SUCCESS);
814 
815   /* A zero size A00 or empty A01 or A10 imply S = A11. */
816   PetscCall(MatGetSize(A00, &N00, NULL));
817   if (!A01 || !A10 || !N00) {
818     if (preuse == MAT_INITIAL_MATRIX) {
819       PetscCall(MatDuplicate(A11, MAT_COPY_VALUES, Sp));
820     } else { /* MAT_REUSE_MATRIX */
821       /* TODO: when can we pass SAME_NONZERO_PATTERN? */
822       PetscCall(MatCopy(A11, *Sp, DIFFERENT_NONZERO_PATTERN));
823     }
824   } else {
825     Mat       AdB, T;
826     Vec       diag;
827     PetscBool flg;
828 
829     if (ainvtype == MAT_SCHUR_COMPLEMENT_AINV_LUMP || ainvtype == MAT_SCHUR_COMPLEMENT_AINV_DIAG) {
830       PetscCall(PetscObjectTypeCompare((PetscObject)A01, MATTRANSPOSEVIRTUAL, &flg));
831       if (flg) {
832         PetscCall(MatTransposeGetMat(A01, &T));
833         PetscCall(MatTranspose(T, MAT_INITIAL_MATRIX, &AdB));
834       } else {
835         PetscCall(PetscObjectTypeCompare((PetscObject)A01, MATHERMITIANTRANSPOSEVIRTUAL, &flg));
836         if (flg) {
837           PetscCall(MatHermitianTransposeGetMat(A01, &T));
838           PetscCall(MatHermitianTranspose(T, MAT_INITIAL_MATRIX, &AdB));
839         }
840       }
841       if (!flg) PetscCall(MatDuplicate(A01, MAT_COPY_VALUES, &AdB));
842       else {
843         PetscScalar shift, scale;
844 
845         PetscCall(MatShellGetScalingShifts(A01, &shift, &scale, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Mat *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED));
846         PetscCall(MatShift(AdB, shift));
847         PetscCall(MatScale(AdB, scale));
848       }
849       PetscCall(MatCreateVecs(A00, &diag, NULL));
850       if (ainvtype == MAT_SCHUR_COMPLEMENT_AINV_LUMP) {
851         PetscCall(MatGetRowSum(A00, diag));
852       } else {
853         PetscCall(MatGetDiagonal(A00, diag));
854       }
855       PetscCall(VecReciprocal(diag));
856       PetscCall(MatDiagonalScale(AdB, diag, NULL));
857       PetscCall(VecDestroy(&diag));
858     } else if (ainvtype == MAT_SCHUR_COMPLEMENT_AINV_BLOCK_DIAG) {
859       Mat      A00_inv;
860       MatType  type;
861       MPI_Comm comm;
862 
863       PetscCall(PetscObjectGetComm((PetscObject)A00, &comm));
864       PetscCall(MatGetType(A00, &type));
865       PetscCall(MatCreate(comm, &A00_inv));
866       PetscCall(MatSetType(A00_inv, type));
867       PetscCall(MatInvertBlockDiagonalMat(A00, A00_inv));
868       PetscCall(MatMatMult(A00_inv, A01, MAT_INITIAL_MATRIX, PETSC_DETERMINE, &AdB));
869       PetscCall(MatDestroy(&A00_inv));
870     } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unknown MatSchurComplementAinvType: %d", ainvtype);
871     /* Cannot really reuse Sp in MatMatMult() because of MatAYPX() -->
872          MatAXPY() --> MatHeaderReplace() --> MatDestroy_XXX_MatMatMult()  */
873     if (preuse == MAT_REUSE_MATRIX) PetscCall(MatDestroy(Sp));
874     PetscCall(MatMatMult(A10, AdB, MAT_INITIAL_MATRIX, PETSC_DETERMINE, Sp));
875     PetscCall(MatScale(*Sp, -1.0));
876     if (A11) { /* TODO: when can we pass SAME_NONZERO_PATTERN? */
877       PetscCall(MatAXPY(*Sp, 1.0, A11, DIFFERENT_NONZERO_PATTERN));
878     }
879     PetscCall(MatDestroy(&AdB));
880   }
881   PetscFunctionReturn(PETSC_SUCCESS);
882 }
883 
MatSchurComplementGetPmat_Basic(Mat S,MatReuse preuse,Mat * Sp)884 static PetscErrorCode MatSchurComplementGetPmat_Basic(Mat S, MatReuse preuse, Mat *Sp)
885 {
886   Mat                  A, B, C, D;
887   Mat_SchurComplement *schur = (Mat_SchurComplement *)S->data;
888   MatNullSpace         sp;
889 
890   PetscFunctionBegin;
891   if (preuse == MAT_IGNORE_MATRIX) PetscFunctionReturn(PETSC_SUCCESS);
892   PetscCall(MatSchurComplementGetSubMatrices(S, &A, NULL, &B, &C, &D));
893   PetscCheck(A, PetscObjectComm((PetscObject)S), PETSC_ERR_ARG_WRONGSTATE, "Schur complement component matrices unset");
894   if (schur->ainvtype != MAT_SCHUR_COMPLEMENT_AINV_FULL) PetscCall(MatCreateSchurComplementPmat(A, B, C, D, schur->ainvtype, preuse, Sp));
895   else {
896     if (preuse == MAT_REUSE_MATRIX) PetscCall(MatDestroy(Sp));
897     PetscCall(MatSchurComplementComputeExplicitOperator(S, Sp));
898   }
899   /* If the Schur complement has a nullspace, then Sp nullspace contains it, independently of the ainv type */
900   PetscCall(MatGetNullSpace(S, &sp));
901   if (sp) PetscCall(MatSetNullSpace(*Sp, sp));
902   PetscCall(MatGetTransposeNullSpace(S, &sp));
903   if (sp) PetscCall(MatSetTransposeNullSpace(*Sp, sp));
904   PetscFunctionReturn(PETSC_SUCCESS);
905 }
906 
907 /*@
908   MatSchurComplementGetPmat - Obtain a matrix for preconditioning the Schur complement by assembling $Sp = A11 - A10 inv(DIAGFORM(A00)) A01$
909 
910   Collective
911 
912   Input Parameters:
913 + S      - matrix obtained with MatCreateSchurComplement() (or equivalent) that implements the action of $A11 - A10 ksp(A00,Ap00) A01$
914 - preuse - `MAT_INITIAL_MATRIX` for a new `Sp`, or `MAT_REUSE_MATRIX` to reuse an existing `Sp`, or `MAT_IGNORE_MATRIX` to put nothing in `Sp`
915 
916   Output Parameter:
917 . Sp - approximate Schur complement suitable for preconditioning the exact Schur complement $S = A11 - A10 inv(A00) A01$
918 
919   Level: advanced
920 
921   Notes:
922   The approximation of `Sp` depends on the argument passed to `MatSchurComplementSetAinvType()`
923   `MAT_SCHUR_COMPLEMENT_AINV_DIAG`, `MAT_SCHUR_COMPLEMENT_AINV_LUMP`, `MAT_SCHUR_COMPLEMENT_AINV_BLOCK_DIAG`, or `MAT_SCHUR_COMPLEMENT_AINV_FULL`
924   -mat_schur_complement_ainv_type <diag,lump,blockdiag,full>
925 
926   Sometimes users would like to provide problem-specific data in the Schur complement, usually only
927   for special row and column index sets.  In that case, the user should call `PetscObjectComposeFunction()` to set
928   "MatSchurComplementGetPmat_C" to their function.  If their function needs to fall back to the default implementation,
929   it should call `MatSchurComplementGetPmat_Basic()`.
930 
931   Developer Notes:
932   The API that includes `MatGetSchurComplement()`, `MatCreateSchurComplement()`, `MatSchurComplementGetPmat()` should be refactored to
933   remove redundancy and be clearer and simpler.
934 
935   This routine should be called `MatSchurComplementCreatePmat()`
936 
937 .seealso: [](ch_ksp), `MatCreateSubMatrix()`, `PCFIELDSPLIT`, `MatGetSchurComplement()`, `MatCreateSchurComplement()`, `MatSchurComplementSetAinvType()`
938 @*/
MatSchurComplementGetPmat(Mat S,MatReuse preuse,Mat * Sp)939 PetscErrorCode MatSchurComplementGetPmat(Mat S, MatReuse preuse, Mat *Sp)
940 {
941   PetscErrorCode (*f)(Mat, MatReuse, Mat *);
942 
943   PetscFunctionBegin;
944   PetscValidHeaderSpecific(S, MAT_CLASSID, 1);
945   PetscValidType(S, 1);
946   PetscValidLogicalCollectiveEnum(S, preuse, 2);
947   if (preuse != MAT_IGNORE_MATRIX) {
948     PetscAssertPointer(Sp, 3);
949     if (preuse == MAT_INITIAL_MATRIX) *Sp = NULL;
950     if (preuse == MAT_REUSE_MATRIX) PetscValidHeaderSpecific(*Sp, MAT_CLASSID, 3);
951   }
952   PetscCheck(!S->factortype, PetscObjectComm((PetscObject)S), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix");
953 
954   PetscCall(PetscObjectQueryFunction((PetscObject)S, "MatSchurComplementGetPmat_C", &f));
955   if (f) PetscCall((*f)(S, preuse, Sp));
956   else PetscCall(MatSchurComplementGetPmat_Basic(S, preuse, Sp));
957   PetscFunctionReturn(PETSC_SUCCESS);
958 }
959 
MatProductNumeric_SchurComplement_Dense(Mat C)960 static PetscErrorCode MatProductNumeric_SchurComplement_Dense(Mat C)
961 {
962   Mat_Product         *product = C->product;
963   Mat_SchurComplement *Na      = (Mat_SchurComplement *)product->A->data;
964   Mat                  work1, work2;
965   PetscScalar         *v;
966   PetscInt             lda;
967 
968   PetscFunctionBegin;
969   PetscCall(MatMatMult(Na->B, product->B, MAT_INITIAL_MATRIX, PETSC_DETERMINE, &work1));
970   PetscCall(MatDuplicate(work1, MAT_DO_NOT_COPY_VALUES, &work2));
971   PetscCall(KSPMatSolve(Na->ksp, work1, work2));
972   PetscCall(MatDestroy(&work1));
973   PetscCall(MatDenseGetArrayWrite(C, &v));
974   PetscCall(MatDenseGetLDA(C, &lda));
975   PetscCall(MatCreateDense(PetscObjectComm((PetscObject)C), C->rmap->n, C->cmap->n, C->rmap->N, C->cmap->N, v, &work1));
976   PetscCall(MatDenseSetLDA(work1, lda));
977   PetscCall(MatMatMult(Na->C, work2, MAT_REUSE_MATRIX, PETSC_DETERMINE, &work1));
978   PetscCall(MatDenseRestoreArrayWrite(C, &v));
979   PetscCall(MatDestroy(&work2));
980   PetscCall(MatDestroy(&work1));
981   if (Na->D) {
982     PetscCall(MatMatMult(Na->D, product->B, MAT_INITIAL_MATRIX, PETSC_DETERMINE, &work1));
983     PetscCall(MatAYPX(C, -1.0, work1, SAME_NONZERO_PATTERN));
984     PetscCall(MatDestroy(&work1));
985   } else PetscCall(MatScale(C, -1.0));
986   PetscFunctionReturn(PETSC_SUCCESS);
987 }
988 
MatProductSymbolic_SchurComplement_Dense(Mat C)989 static PetscErrorCode MatProductSymbolic_SchurComplement_Dense(Mat C)
990 {
991   Mat_Product *product = C->product;
992   Mat          A = product->A, B = product->B;
993   PetscInt     m = A->rmap->n, n = B->cmap->n, M = A->rmap->N, N = B->cmap->N;
994   PetscBool    flg;
995 
996   PetscFunctionBegin;
997   PetscCall(MatSetSizes(C, m, n, M, N));
998   PetscCall(PetscObjectBaseTypeCompareAny((PetscObject)C, &flg, MATSEQDENSE, MATMPIDENSE, ""));
999   if (!flg) {
1000     PetscCall(MatSetType(C, ((PetscObject)B)->type_name));
1001     C->ops->productsymbolic = MatProductSymbolic_SchurComplement_Dense;
1002   }
1003   PetscCall(MatSetUp(C));
1004   C->ops->productnumeric = MatProductNumeric_SchurComplement_Dense;
1005   PetscFunctionReturn(PETSC_SUCCESS);
1006 }
1007 
MatProductSetFromOptions_SchurComplement_Dense(Mat C)1008 static PetscErrorCode MatProductSetFromOptions_SchurComplement_Dense(Mat C)
1009 {
1010   Mat_Product *product = C->product;
1011 
1012   PetscFunctionBegin;
1013   if (product->type != MATPRODUCT_AB) PetscFunctionReturn(PETSC_SUCCESS);
1014   C->ops->productsymbolic = MatProductSymbolic_SchurComplement_Dense;
1015   PetscFunctionReturn(PETSC_SUCCESS);
1016 }
1017 
MatProductNumeric_SchurComplement_Any(Mat C)1018 static PetscErrorCode MatProductNumeric_SchurComplement_Any(Mat C)
1019 {
1020   Mat_SchurComplement *Na = (Mat_SchurComplement *)C->data;
1021 
1022   PetscFunctionBegin;
1023   if (Na->D && Na->D->product) PetscCall(MatProductNumeric(Na->D));
1024   if (Na->B->product) PetscCall(MatProductNumeric(Na->B));
1025   if (Na->C->product) PetscCall(MatProductNumeric(Na->C));
1026   C->assembled = PETSC_TRUE;
1027   PetscFunctionReturn(PETSC_SUCCESS);
1028 }
1029 
MatProductSymbolic_SchurComplement_Any(Mat C)1030 static PetscErrorCode MatProductSymbolic_SchurComplement_Any(Mat C)
1031 {
1032   Mat_SchurComplement *Na = (Mat_SchurComplement *)C->data;
1033 
1034   PetscFunctionBegin;
1035   if (Na->D && Na->D->product) PetscCall(MatProductSymbolic(Na->D));
1036   if (Na->B->product) PetscCall(MatProductSymbolic(Na->B));
1037   if (Na->C->product) PetscCall(MatProductSymbolic(Na->C));
1038   C->ops->productnumeric = MatProductNumeric_SchurComplement_Any;
1039   C->preallocated        = PETSC_TRUE;
1040   C->assembled           = PETSC_FALSE;
1041   PetscFunctionReturn(PETSC_SUCCESS);
1042 }
1043 
MatProductSetFromOptions_SchurComplement_Any(Mat C)1044 static PetscErrorCode MatProductSetFromOptions_SchurComplement_Any(Mat C)
1045 {
1046   Mat_Product         *product = C->product;
1047   Mat_SchurComplement *Na, *Ca;
1048   Mat                  B = product->B, S = product->A, pB = NULL, pC = NULL, pD = NULL;
1049   KSP                  ksp;
1050   PetscInt             m = PETSC_DECIDE, n = PETSC_DECIDE, M = PETSC_DECIDE, N = PETSC_DECIDE;
1051   MatProductType       pbtype = MATPRODUCT_UNSPECIFIED, pctype = MATPRODUCT_UNSPECIFIED;
1052   PetscBool            isschur;
1053 
1054   PetscFunctionBegin;
1055   if (product->type == MATPRODUCT_ABC || product->type == MATPRODUCT_AtB) PetscFunctionReturn(PETSC_SUCCESS);
1056   /* A * S not yet supported (should be easy though) */
1057   PetscCall(PetscObjectTypeCompare((PetscObject)S, MATSCHURCOMPLEMENT, &isschur));
1058   if (!isschur) PetscFunctionReturn(PETSC_SUCCESS);
1059 
1060   Na = (Mat_SchurComplement *)S->data;
1061   if (Na->D) {
1062     PetscCall(MatProductCreate(Na->D, B, NULL, &pD));
1063     PetscCall(MatProductSetType(pD, product->type));
1064     PetscCall(MatProductSetFromOptions(pD));
1065   }
1066   if (pD && !pD->ops->productsymbolic) {
1067     PetscCall(MatDestroy(&pD));
1068     PetscFunctionReturn(PETSC_SUCCESS);
1069   }
1070 
1071   /* S = A11 - A10 M A01 */
1072   switch (product->type) {
1073   case MATPRODUCT_AB: /* A11 B - A10 * M * A01 * B */
1074     pbtype = product->type;
1075     PetscCall(PetscObjectReference((PetscObject)Na->C));
1076     pC = Na->C;
1077     m  = S->rmap->n;
1078     M  = S->rmap->N;
1079     n  = B->cmap->n;
1080     N  = B->cmap->N;
1081     break;
1082   case MATPRODUCT_ABt: /* A11 B^t - A10 * M * A01 * B^t */
1083     pbtype = product->type;
1084     PetscCall(PetscObjectReference((PetscObject)Na->C));
1085     pC = Na->C;
1086     m  = S->rmap->n;
1087     M  = S->rmap->N;
1088     n  = B->rmap->n;
1089     N  = B->rmap->N;
1090     break;
1091   case MATPRODUCT_PtAP: /* Pt A11 P - Pt * A10 * M * A01 * P */
1092     pbtype = MATPRODUCT_AB;
1093     pctype = MATPRODUCT_AtB;
1094     m      = B->cmap->n;
1095     M      = B->cmap->N;
1096     n      = B->cmap->n;
1097     N      = B->cmap->N;
1098     break;
1099   case MATPRODUCT_RARt: /* R A11 Rt - R * A10 * M * A01 * Rt */
1100     pbtype = MATPRODUCT_ABt;
1101     pctype = MATPRODUCT_AB;
1102     m      = B->rmap->n;
1103     M      = B->rmap->N;
1104     n      = B->rmap->n;
1105     N      = B->rmap->N;
1106     break;
1107   default:
1108     break;
1109   }
1110   PetscCall(MatProductCreate(Na->B, B, NULL, &pB));
1111   PetscCall(MatProductSetType(pB, pbtype));
1112   PetscCall(MatProductSetFromOptions(pB));
1113   if (!pB->ops->productsymbolic) {
1114     PetscCall(MatDestroy(&pB));
1115     PetscFunctionReturn(PETSC_SUCCESS);
1116   }
1117   if (pC == NULL) { /* Some work can in principle be saved here if we recognize symmetry */
1118     PetscCall(MatProductCreate(B, Na->C, NULL, &pC));
1119     PetscCall(MatProductSetType(pC, pctype));
1120     PetscCall(MatProductSetFromOptions(pC));
1121     if (!pC->ops->productsymbolic) {
1122       PetscCall(MatDestroy(&pC));
1123       PetscFunctionReturn(PETSC_SUCCESS);
1124     }
1125   }
1126   PetscCall(MatSetType(C, MATSCHURCOMPLEMENT));
1127   PetscCall(MatSetSizes(C, m, n, M, N));
1128   PetscCall(PetscLayoutSetUp(C->rmap));
1129   PetscCall(PetscLayoutSetUp(C->cmap));
1130   PetscCall(PetscObjectReference((PetscObject)Na->A));
1131   PetscCall(PetscObjectReference((PetscObject)Na->Ap));
1132   Ca                      = (Mat_SchurComplement *)C->data;
1133   Ca->A                   = Na->A;
1134   Ca->Ap                  = Na->Ap;
1135   Ca->B                   = pB;
1136   Ca->C                   = pC;
1137   Ca->D                   = pD;
1138   C->ops->productsymbolic = MatProductSymbolic_SchurComplement_Any;
1139   PetscCall(MatSchurComplementGetKSP(S, &ksp));
1140   PetscCall(MatSchurComplementSetKSP(C, ksp));
1141   PetscFunctionReturn(PETSC_SUCCESS);
1142 }
1143 
1144 /*MC
1145   MATSCHURCOMPLEMENT -  "schurcomplement" - Matrix type that behaves like the Schur complement of a matrix.
1146 
1147   Level: intermediate
1148 
1149 .seealso: [](ch_matrices), `Mat`, `MatCreate()`, `MatType`, `MatCreateSchurComplement()`, `MatSchurComplementComputeExplicitOperator()`,
1150           `MatSchurComplementGetSubMatrices()`, `MatSchurComplementGetKSP()`
1151 M*/
MatCreate_SchurComplement(Mat N)1152 PETSC_EXTERN PetscErrorCode MatCreate_SchurComplement(Mat N)
1153 {
1154   Mat_SchurComplement *Na;
1155 
1156   PetscFunctionBegin;
1157   PetscCall(PetscNew(&Na));
1158   N->data = (void *)Na;
1159 
1160   N->ops->destroy        = MatDestroy_SchurComplement;
1161   N->ops->getvecs        = MatCreateVecs_SchurComplement;
1162   N->ops->view           = MatView_SchurComplement;
1163   N->ops->mult           = MatMult_SchurComplement;
1164   N->ops->multtranspose  = MatMultTranspose_SchurComplement;
1165   N->ops->multadd        = MatMultAdd_SchurComplement;
1166   N->ops->setfromoptions = MatSetFromOptions_SchurComplement;
1167   N->assembled           = PETSC_FALSE;
1168   N->preallocated        = PETSC_FALSE;
1169 
1170   PetscCall(KSPCreate(PetscObjectComm((PetscObject)N), &Na->ksp));
1171   PetscCall(PetscObjectChangeTypeName((PetscObject)N, MATSCHURCOMPLEMENT));
1172   PetscCall(PetscObjectComposeFunction((PetscObject)N, "MatProductSetFromOptions_schurcomplement_seqdense_C", MatProductSetFromOptions_SchurComplement_Dense));
1173   PetscCall(PetscObjectComposeFunction((PetscObject)N, "MatProductSetFromOptions_schurcomplement_mpidense_C", MatProductSetFromOptions_SchurComplement_Dense));
1174   PetscCall(PetscObjectComposeFunction((PetscObject)N, "MatProductSetFromOptions_anytype_C", MatProductSetFromOptions_SchurComplement_Any));
1175   PetscFunctionReturn(PETSC_SUCCESS);
1176 }
1177