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