Actual source code: schurm.c

  1: #include <../src/ksp/ksp/utils/schurm/schurm.h>

  3: const char *const MatSchurComplementAinvTypes[] = {"DIAG", "LUMP", "BLOCKDIAG", "FULL", "MatSchurComplementAinvType", "MAT_SCHUR_COMPLEMENT_AINV_", NULL};

  5: PetscErrorCode MatCreateVecs_SchurComplement(Mat N, Vec *right, Vec *left)
  6: {
  7:   Mat_SchurComplement *Na = (Mat_SchurComplement *)N->data;

  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: }

 19: PetscErrorCode MatView_SchurComplement(Mat N, PetscViewer viewer)
 20: {
 21:   Mat_SchurComplement *Na = (Mat_SchurComplement *)N->data;

 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: }

 45: /*
 46:            A11^T - A01^T ksptrans(A00,Ap00) A10^T
 47: */
 48: PetscErrorCode MatMultTranspose_SchurComplement(Mat N, Vec x, Vec y)
 49: {
 50:   Mat_SchurComplement *Na = (Mat_SchurComplement *)N->data;

 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: }

 63: /*
 64:            A11 - A10 ksp(A00,Ap00) A01
 65: */
 66: PetscErrorCode MatMult_SchurComplement(Mat N, Vec x, Vec y)
 67: {
 68:   Mat_SchurComplement *Na = (Mat_SchurComplement *)N->data;

 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: }

 81: /*
 82:            A11 - A10 ksp(A00,Ap00) A01
 83: */
 84: PetscErrorCode MatMultAdd_SchurComplement(Mat N, Vec x, Vec y, Vec z)
 85: {
 86:   Mat_SchurComplement *Na = (Mat_SchurComplement *)N->data;

 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: }

104: PetscErrorCode MatSetFromOptions_SchurComplement(Mat N, PetscOptionItems PetscOptionsObject)
105: {
106:   Mat_SchurComplement *Na = (Mat_SchurComplement *)N->data;

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: }

118: PetscErrorCode MatDestroy_SchurComplement(Mat N)
119: {
120:   Mat_SchurComplement *Na = (Mat_SchurComplement *)N->data;

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: }

138: /*@
139:   MatCreateSchurComplement - Creates a new `Mat` that behaves like the Schur complement of a matrix

141:   Collective

143:   Input Parameters:
144: + A00  - the upper-left block of the original matrix $A = [A00 A01; A10 A11]$
145: . Ap00 - preconditioning matrix 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]$

150:   Output Parameter:
151: . S - the matrix that behaves as the Schur complement $S = A11 - A10 ksp(A00,Ap00) A01$

153:   Level: intermediate

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}$.

160:   All four matrices must have the same MPI communicator.

162:   `A00` and  `A11` must be square matrices.

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()`

168:   Developer Notes:
169:   The API that includes `MatGetSchurComplement()`, `MatCreateSchurComplement()`, `MatSchurComplementGetPmat()` should be refactored to
170:   remove redundancy and be clearer and simpler.

172: .seealso: [](ch_ksp), `MatCreateNormal()`, `MatMult()`, `MatCreate()`, `MatSchurComplementGetKSP()`, `MatSchurComplementUpdateSubMatrices()`, `MatCreateTranspose()`, `MatGetSchurComplement()`,
173:           `MatSchurComplementGetPmat()`, `MatSchurComplementSetSubMatrices()`
174: @*/
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: }

185: /*@
186:   MatSchurComplementSetSubMatrices - Sets the matrices that define the Schur complement

188:   Collective

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 - preconditioning matrix 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]$

198:   Level: intermediate

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$

204:   All four matrices must have the same MPI communicator.

206:   `A00` and `A11` must be square matrices.

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()`

215: .seealso: [](ch_ksp), `Mat`, `MatCreateNormal()`, `MatMult()`, `MatCreate()`, `MatSchurComplementGetKSP()`, `MatSchurComplementUpdateSubMatrices()`, `MatCreateTranspose()`, `MatCreateSchurComplement()`, `MatGetSchurComplement()`
216: @*/
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;

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");
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) {
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:   }

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: }

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$

264:   Not Collective

266:   Input Parameter:
267: . S - matrix obtained with `MatCreateSchurComplement()` (or equivalent) and implementing the action of $ A11 - A10 ksp(A00,Ap00) A01 $

269:   Output Parameter:
270: . ksp - the linear solver object

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.

275:   Level: intermediate

277: .seealso: [](ch_ksp), `Mat`, `MatSchurComplementSetKSP()`, `MatCreateSchurComplement()`, `MatCreateNormal()`, `MatMult()`, `MatCreate()`
278: @*/
279: PetscErrorCode MatSchurComplementGetKSP(Mat S, KSP *ksp)
280: {
281:   Mat_SchurComplement *Na;
282:   PetscBool            isschur;

284:   PetscFunctionBegin;
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: }

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$

297:   Not Collective

299:   Input Parameters:
300: + S   - matrix created with `MatCreateSchurComplement()`
301: - ksp - the linear solver object

303:   Level: developer

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`.

309: .seealso: [](ch_ksp), `Mat`, `MatSchurComplementGetKSP()`, `MatCreateSchurComplement()`, `MatCreateNormal()`, `MatMult()`, `MatCreate()`, `MATSCHURCOMPLEMENT`
310: @*/
311: PetscErrorCode MatSchurComplementSetKSP(Mat S, KSP ksp)
312: {
313:   Mat_SchurComplement *Na;
314:   PetscBool            isschur;

316:   PetscFunctionBegin;
318:   PetscCall(PetscObjectTypeCompare((PetscObject)S, MATSCHURCOMPLEMENT, &isschur));
319:   if (!isschur) PetscFunctionReturn(PETSC_SUCCESS);
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: }

329: /*@
330:   MatSchurComplementUpdateSubMatrices - Updates the Schur complement matrix object with new submatrices

332:   Collective

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 - preconditioning matrix 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]$

342:   Level: intermediate

344:   Notes:
345:   All four matrices must have the same MPI communicator

347:   `A00` and  `A11` must be square matrices

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.

352:   This can only be called after `MatCreateSchurComplement()` or `MatSchurComplementSetSubMatrices()`, it cannot be called immediately after `MatSetType`(S,`MATSCHURCOMPLEMENT`);

354:   Developer Notes:
355:   This code is almost identical to `MatSchurComplementSetSubMatrices()`. The API should be refactored.

357: .seealso: [](ch_ksp), `Mat`, `MatCreateNormal()`, `MatMult()`, `MatCreate()`, `MatSchurComplementGetKSP()`, `MatCreateSchurComplement()`
358: @*/
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;

364:   PetscFunctionBegin;
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");
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) {
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:   }

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));

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));

399:   Na->A  = A00;
400:   Na->Ap = Ap00;
401:   Na->B  = A01;
402:   Na->C  = A10;
403:   Na->D  = A11;

405:   PetscCall(KSPSetOperators(Na->ksp, A00, Ap00));
406:   PetscFunctionReturn(PETSC_SUCCESS);
407: }

409: /*@
410:   MatSchurComplementGetSubMatrices - Get the individual submatrices in the Schur complement

412:   Collective

414:   Input Parameter:
415: . S - matrix obtained with `MatCreateSchurComplement()` (or equivalent) and implementing the action of $A11 - A10 ksp(A00,Ap00) A01$

417:   Output Parameters:
418: + A00  - the upper-left block of the original matrix $A = [A00 A01; A10 A11]$
419: . Ap00 - preconditioning matrix 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]$

424:   Level: intermediate

426:   Note:
427:   Use `NULL` for any unneeded output argument.

429:   The reference counts of the submatrices are not increased before they are returned and the matrices should not be modified or destroyed.

431: .seealso: [](ch_ksp), `MatCreateNormal()`, `MatMult()`, `MatCreate()`, `MatSchurComplementGetKSP()`, `MatCreateSchurComplement()`, `MatSchurComplementUpdateSubMatrices()`
432: @*/
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;

438:   PetscFunctionBegin;
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: }

450: #include <petsc/private/kspimpl.h>

452: /*@
453:   MatSchurComplementComputeExplicitOperator - Compute the Schur complement matrix explicitly

455:   Collective

457:   Input Parameter:
458: . A - the matrix obtained with `MatCreateSchurComplement()`

460:   Output Parameter:
461: . S - the Schur complement matrix

463:   Level: advanced

465:   Notes:
466:   This can be expensive when `S` is large, so it is mainly for testing

468:   Use `MatSchurComplementGetPmat()` to get a sparse approximation for the Schur complement suitable for use in building a preconditioner

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`

474:   Developer Note:
475:   The three aforementioned should not be parsed and used in this routine, but rather in `MatSetFromOptions()`

477: .seealso: [](ch_ksp), `MatCreateSchurComplement()`, `MatSchurComplementUpdateSubMatrices()`, `MatSchurComplementGetPmat()`
478: @*/
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];

489:   PetscFunctionBegin;
490:   PetscCall(PetscObjectQuery((PetscObject)A, "AinvB", (PetscObject *)&AinvBd));
491:   set = (PetscBool)(AinvBd != NULL);
492:   if (set && AinvBd->cmap->N == -1) PetscFunctionReturn(PETSC_SUCCESS); // early bail out if composed Mat is uninitialized
493:   PetscCall(MatSchurComplementGetSubMatrices(A, &P, NULL, &B, &C, &D));
494:   PetscCall(MatGetVecType(B, &vtype));
495:   PetscCall(MatGetLocalSize(B, &m, &n));
496:   PetscCall(MatSchurComplementGetKSP(A, &ksp));
497:   PetscCall(KSPSetUp(ksp));
498:   if (set) {
499:     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);
500:     PetscCall(MatGetType(AinvBd, &mtype));
501:     if (AinvBd->cmap->N > A->cmap->N) {
502:       Mat s[2];

504:       PetscCall(MatDuplicate(AinvBd, MAT_DO_NOT_COPY_VALUES, &Bd));
505:       PetscCall(MatDenseGetSubMatrix(Bd, PETSC_DECIDE, PETSC_DECIDE, A->cmap->N, AinvBd->cmap->N, s));
506:       PetscCall(MatDenseGetSubMatrix(AinvBd, PETSC_DECIDE, PETSC_DECIDE, A->cmap->N, AinvBd->cmap->N, s + 1));
507:       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()
508:       PetscCall(MatDenseRestoreSubMatrix(AinvBd, s + 1));
509:       PetscCall(MatDenseRestoreSubMatrix(Bd, s));
510:       PetscCall(MatDenseGetSubMatrix(Bd, PETSC_DECIDE, PETSC_DECIDE, 0, A->cmap->N, &sub));
511:       PetscCall(MatConvert(B, mtype, MAT_REUSE_MATRIX, &sub)); // copy A01 into the first columns of the block of RHS of KSPMatSolve()
512:       PetscCall(MatDenseRestoreSubMatrix(Bd, &sub));
513:     } else PetscCall(MatConvert(B, mtype, MAT_INITIAL_MATRIX, &Bd));
514:   } else {
515:     PetscCall(MatGetSize(B, &M, &N));
516:     PetscCall(MatCreateDenseFromVecType(PetscObjectComm((PetscObject)A), vtype, m, n, M, N, -1, NULL, &AinvBd));
517:     PetscCall(MatGetType(AinvBd, &mtype));
518:     PetscCall(MatConvert(B, mtype, MAT_INITIAL_MATRIX, &Bd));
519:   }
520:   PetscCall(KSPMatSolve(ksp, Bd, AinvBd));
521:   if (set && AinvBd->cmap->N > A->cmap->N) {
522:     Mat          AinvB;
523:     PetscScalar *v;
524:     PetscBool    match;

526:     PetscCall(PetscObjectTypeCompareAny((PetscObject)AinvBd, &match, MATSEQDENSECUDA, MATMPIDENSECUDA, ""));
527:     if (match) {
528: #if PetscDefined(HAVE_CUDA)
529:       PetscCall(MatDenseCUDAGetArrayWrite(AinvBd, &v));
530:       PetscCall(MatCreateDenseCUDA(PetscObjectComm((PetscObject)A), AinvBd->rmap->n, A->cmap->n, AinvBd->rmap->N, A->cmap->N, v, &AinvB));
531:       PetscCall(MatDenseCUDAReplaceArray(AinvB, v));
532:       PetscCall(MatDenseCUDARestoreArrayWrite(AinvBd, &v));
533: #endif
534:     } else {
535:       PetscCall(PetscObjectTypeCompareAny((PetscObject)AinvBd, &match, MATSEQDENSEHIP, MATMPIDENSEHIP, ""));
536:       if (match) {
537: #if PetscDefined(HAVE_HIP)
538:         PetscCall(MatDenseHIPGetArrayWrite(AinvBd, &v));
539:         PetscCall(MatCreateDenseHIP(PetscObjectComm((PetscObject)A), AinvBd->rmap->n, A->cmap->n, AinvBd->rmap->N, A->cmap->N, v, &AinvB));
540:         PetscCall(MatDenseHIPReplaceArray(AinvB, v));
541:         PetscCall(MatDenseHIPRestoreArrayWrite(AinvBd, &v));
542: #endif
543:       } else {
544:         PetscCall(MatDenseGetArrayWrite(AinvBd, &v)); // no easy way to resize a Mat, so create a new one with the same data pointer
545:         PetscCall(MatCreateDense(PetscObjectComm((PetscObject)A), AinvBd->rmap->n, A->cmap->n, AinvBd->rmap->N, A->cmap->N, v, &AinvB));
546:         PetscCall(MatDenseReplaceArray(AinvB, v)); // let MatDestroy() free the data pointer
547:         PetscCall(MatDenseRestoreArrayWrite(AinvBd, &v));
548:       }
549:     }
550:     PetscCall(MatHeaderReplace(AinvBd, &AinvB)); // replace the input composed Mat with just A00^-1 A01 (trailing columns are removed)
551:   }
552:   PetscCall(MatDestroy(&Bd));
553:   if (!set) PetscCall(MatFilter(AinvBd, PETSC_SMALL, PETSC_FALSE, PETSC_FALSE));
554:   if (D) {
555:     PetscCall(MatGetLocalSize(D, &m, &n));
556:     PetscCall(MatGetSize(D, &M, &N));
557:     PetscCall(MatCreateDenseFromVecType(PetscObjectComm((PetscObject)A), vtype, m, n, M, N, -1, NULL, S));
558:   }
559:   PetscCall(MatMatMult(C, AinvBd, D ? MAT_REUSE_MATRIX : MAT_INITIAL_MATRIX, PETSC_DETERMINE, S));
560:   if (!set) PetscCall(MatDestroy(&AinvBd));
561:   else {
562:     PetscCall(MatScale(AinvBd, -1.0));
563:     PetscCall(MatFilter(AinvBd, PETSC_MACHINE_EPSILON, PETSC_FALSE, PETSC_FALSE));
564:     PetscCall(MatFilter(*S, PETSC_MACHINE_EPSILON, PETSC_FALSE, PETSC_FALSE));
565:   }
566:   if (D) {
567:     PetscCall(PetscObjectTypeCompareAny((PetscObject)D, &flg, MATSEQSBAIJ, MATMPISBAIJ, ""));
568:     if (flg) {
569:       PetscCall(MatIsSymmetricKnown(A, &set, &symm));
570:       if (!set || !symm) PetscCall(MatConvert(D, MATBAIJ, MAT_INITIAL_MATRIX, &E)); /* convert the (1,1) block to nonsymmetric storage for MatAXPY() */
571:     }
572:     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 */
573:     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 */
574:   }
575:   PetscCall(MatDestroy(&E));
576:   PetscCall(MatScale(*S, -1.0));
577:   PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "%sexplicit_operator_", ((PetscObject)A)->prefix ? ((PetscObject)A)->prefix : ""));
578:   PetscCall(MatSetOptionsPrefix(*S, prefix));
579:   PetscObjectOptionsBegin((PetscObject)*S);
580:   PetscCall(PetscOptionsFList("-mat_type", "Matrix type", "MatSetType", MatList, !E && flg ? MATSBAIJ : mtype, type, 256, &set));
581:   if (set) PetscCall(MatConvert(*S, type, MAT_INPLACE_MATRIX, S));
582:   flg = PETSC_FALSE;
583:   PetscCall(PetscOptionsBool("-mat_symmetric", "Sets the MAT_SYMMETRIC option", "MatSetOption", flg, &flg, &set));
584:   if (set) PetscCall(MatSetOption(*S, MAT_SYMMETRIC, flg));
585:   if (PetscDefined(USE_COMPLEX)) {
586:     flg = PETSC_FALSE;
587:     PetscCall(PetscOptionsBool("-mat_hermitian", "Sets the MAT_HERMITIAN option", "MatSetOption", flg, &flg, &set));
588:     if (set) PetscCall(MatSetOption(*S, MAT_HERMITIAN, flg));
589:   }
590:   PetscOptionsEnd();
591:   PetscFunctionReturn(PETSC_SUCCESS);
592: }

594: /* Developer Notes:
595:     This should be implemented with a MatCreate_SchurComplement() as that is the standard design for new Mat classes. */
596: PetscErrorCode MatGetSchurComplement_Basic(Mat mat, IS isrow0, IS iscol0, IS isrow1, IS iscol1, MatReuse mreuse, Mat *S, MatSchurComplementAinvType ainvtype, MatReuse preuse, Mat *Sp)
597: {
598:   Mat      A = NULL, Ap = NULL, B = NULL, C = NULL, D = NULL;
599:   MatReuse reuse;

601:   PetscFunctionBegin;
611:   if (mreuse == MAT_IGNORE_MATRIX && preuse == MAT_IGNORE_MATRIX) PetscFunctionReturn(PETSC_SUCCESS);

615:   PetscCheck(!mat->factortype, PetscObjectComm((PetscObject)mat), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix");

617:   reuse = MAT_INITIAL_MATRIX;
618:   if (mreuse == MAT_REUSE_MATRIX) {
619:     PetscCall(MatSchurComplementGetSubMatrices(*S, &A, &Ap, &B, &C, &D));
620:     PetscCheck(A && Ap && B && C, PetscObjectComm((PetscObject)mat), PETSC_ERR_ARG_WRONGSTATE, "Attempting to reuse matrix but Schur complement matrices unset");
621:     PetscCheck(A == Ap, PetscObjectComm((PetscObject)mat), PETSC_ERR_ARG_WRONGSTATE, "Preconditioning matrix does not match operator");
622:     PetscCall(MatDestroy(&Ap)); /* get rid of extra reference */
623:     reuse = MAT_REUSE_MATRIX;
624:   }
625:   PetscCall(MatCreateSubMatrix(mat, isrow0, iscol0, reuse, &A));
626:   PetscCall(MatCreateSubMatrix(mat, isrow0, iscol1, reuse, &B));
627:   PetscCall(MatCreateSubMatrix(mat, isrow1, iscol0, reuse, &C));
628:   PetscCall(MatCreateSubMatrix(mat, isrow1, iscol1, reuse, &D));
629:   switch (mreuse) {
630:   case MAT_INITIAL_MATRIX:
631:     PetscCall(MatCreateSchurComplement(A, A, B, C, D, S));
632:     break;
633:   case MAT_REUSE_MATRIX:
634:     PetscCall(MatSchurComplementUpdateSubMatrices(*S, A, A, B, C, D));
635:     break;
636:   default:
637:     PetscCheck(mreuse == MAT_IGNORE_MATRIX, PetscObjectComm((PetscObject)mat), PETSC_ERR_SUP, "Unrecognized value of mreuse %d", (int)mreuse);
638:   }
639:   if (preuse != MAT_IGNORE_MATRIX) PetscCall(MatCreateSchurComplementPmat(A, B, C, D, ainvtype, preuse, Sp));
640:   PetscCall(MatDestroy(&A));
641:   PetscCall(MatDestroy(&B));
642:   PetscCall(MatDestroy(&C));
643:   PetscCall(MatDestroy(&D));
644:   PetscFunctionReturn(PETSC_SUCCESS);
645: }

647: /*@
648:   MatGetSchurComplement - Obtain the Schur complement from eliminating part of the matrix in another part.

650:   Collective

652:   Input Parameters:
653: + A        - matrix in which the complement is to be taken
654: . isrow0   - rows to eliminate
655: . iscol0   - columns to eliminate, (isrow0,iscol0) should be square and nonsingular
656: . isrow1   - rows in which the Schur complement is formed
657: . iscol1   - columns in which the Schur complement is formed
658: . mreuse   - `MAT_INITIAL_MATRIX` or `MAT_REUSE_MATRIX`, use `MAT_IGNORE_MATRIX` to put nothing in `S`
659: . ainvtype - the type of approximation used for the inverse of the (0,0) block used in forming `Sp`:
660:              `MAT_SCHUR_COMPLEMENT_AINV_DIAG`, `MAT_SCHUR_COMPLEMENT_AINV_LUMP`, `MAT_SCHUR_COMPLEMENT_AINV_BLOCK_DIAG`, or `MAT_SCHUR_COMPLEMENT_AINV_FULL`
661: - preuse   - `MAT_INITIAL_MATRIX` or `MAT_REUSE_MATRIX`, use `MAT_IGNORE_MATRIX` to put nothing in `Sp`

663:   Output Parameters:
664: + S  - exact Schur complement, often of type `MATSCHURCOMPLEMENT` which is difficult to use for preconditioning
665: - Sp - approximate Schur complement from which a preconditioner can be built $A11 - A10 inv(DIAGFORM(A00)) A01$

667:   Level: advanced

669:   Notes:
670:   Since the real Schur complement is usually dense, providing a good approximation to `Sp` usually requires
671:   application-specific information.

673:   Sometimes users would like to provide problem-specific data in the Schur complement, usually only for special row
674:   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
675:   "MatGetSchurComplement_C" to their function.  If their function needs to fall back to the default implementation, it
676:   should call `MatGetSchurComplement_Basic()`.

678:   `MatCreateSchurComplement()` takes as arguments the four submatrices and returns the virtual Schur complement (what this function returns in S).

680:   `MatSchurComplementGetPmat()` takes the virtual Schur complement and returns an explicit approximate Schur complement (what this returns in Sp).

682:   In other words calling `MatCreateSchurComplement()` followed by `MatSchurComplementGetPmat()` produces the same output as this function but with slightly different
683:   inputs. The actually submatrices of the original block matrix instead of index sets to the submatrices.

685:   Developer Notes:
686:   The API that includes `MatGetSchurComplement()`, `MatCreateSchurComplement()`, `MatSchurComplementGetPmat()` should be refactored to
687:   remove redundancy and be clearer and simpler.

689: .seealso: [](ch_ksp), `MatCreateSubMatrix()`, `PCFIELDSPLIT`, `MatCreateSchurComplement()`, `MatSchurComplementAinvType`
690: @*/
691: PetscErrorCode MatGetSchurComplement(Mat A, IS isrow0, IS iscol0, IS isrow1, IS iscol1, MatReuse mreuse, Mat *S, MatSchurComplementAinvType ainvtype, MatReuse preuse, Mat *Sp)
692: {
693:   PetscErrorCode (*f)(Mat, IS, IS, IS, IS, MatReuse, Mat *, MatReuse, Mat *) = NULL;

695:   PetscFunctionBegin;
707:   PetscCheck(!A->factortype, PetscObjectComm((PetscObject)A), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix");
708:   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. */
709:     PetscCall(PetscObjectQueryFunction((PetscObject)*S, "MatGetSchurComplement_C", &f));
710:   }
711:   if (f) PetscCall((*f)(A, isrow0, iscol0, isrow1, iscol1, mreuse, S, preuse, Sp));
712:   else PetscCall(MatGetSchurComplement_Basic(A, isrow0, iscol0, isrow1, iscol1, mreuse, S, ainvtype, preuse, Sp));
713:   PetscFunctionReturn(PETSC_SUCCESS);
714: }

716: /*@
717:   MatSchurComplementSetAinvType - set the type of approximation used for the inverse of the (0,0) block used in forming `Sp` in `MatSchurComplementGetPmat()`

719:   Not Collective

721:   Input Parameters:
722: + S        - matrix obtained with `MatCreateSchurComplement()` (or equivalent) and implementing the action of $A11 - A10 ksp(A00,Ap00) A01$
723: - ainvtype - type of approximation to be used to form approximate Schur complement $Sp = A11 - A10 inv(DIAGFORM(A00)) A01$:
724:              `MAT_SCHUR_COMPLEMENT_AINV_DIAG`, `MAT_SCHUR_COMPLEMENT_AINV_LUMP`, `MAT_SCHUR_COMPLEMENT_AINV_BLOCK_DIAG`, or `MAT_SCHUR_COMPLEMENT_AINV_FULL`

726:   Options Database Key:
727: . -mat_schur_complement_ainv_type diag | lump | blockdiag | full - set schur complement type

729:   Level: advanced

731: .seealso: [](ch_ksp), `MatSchurComplementAinvType`, `MatCreateSchurComplement()`, `MatGetSchurComplement()`, `MatSchurComplementGetPmat()`, `MatSchurComplementGetAinvType()`
732: @*/
733: PetscErrorCode MatSchurComplementSetAinvType(Mat S, MatSchurComplementAinvType ainvtype)
734: {
735:   PetscBool            isschur;
736:   Mat_SchurComplement *schur;

738:   PetscFunctionBegin;
740:   PetscCall(PetscObjectTypeCompare((PetscObject)S, MATSCHURCOMPLEMENT, &isschur));
741:   if (!isschur) PetscFunctionReturn(PETSC_SUCCESS);
743:   schur = (Mat_SchurComplement *)S->data;
744:   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);
745:   schur->ainvtype = ainvtype;
746:   PetscFunctionReturn(PETSC_SUCCESS);
747: }

749: /*@
750:   MatSchurComplementGetAinvType - get the type of approximation for the inverse of the (0,0) block used in forming `Sp` in `MatSchurComplementGetPmat()`

752:   Not Collective

754:   Input Parameter:
755: . S - matrix obtained with `MatCreateSchurComplement()` (or equivalent) and implementing the action of $A11 - A10 ksp(A00,Ap00) A01$

757:   Output Parameter:
758: . ainvtype - type of approximation used to form approximate Schur complement Sp = A11 - A10 inv(DIAGFORM(A00)) A01:
759:              `MAT_SCHUR_COMPLEMENT_AINV_DIAG`, `MAT_SCHUR_COMPLEMENT_AINV_LUMP`, `MAT_SCHUR_COMPLEMENT_AINV_BLOCK_DIAG`, or `MAT_SCHUR_COMPLEMENT_AINV_FULL`

761:   Level: advanced

763: .seealso: [](ch_ksp), `MatSchurComplementAinvType`, `MatCreateSchurComplement()`, `MatGetSchurComplement()`, `MatSchurComplementGetPmat()`, `MatSchurComplementSetAinvType()`
764: @*/
765: PetscErrorCode MatSchurComplementGetAinvType(Mat S, MatSchurComplementAinvType *ainvtype)
766: {
767:   PetscBool            isschur;
768:   Mat_SchurComplement *schur;

770:   PetscFunctionBegin;
772:   PetscCall(PetscObjectTypeCompare((PetscObject)S, MATSCHURCOMPLEMENT, &isschur));
773:   PetscCheck(isschur, PetscObjectComm((PetscObject)S), PETSC_ERR_ARG_WRONG, "Not for type %s", ((PetscObject)S)->type_name);
774:   schur = (Mat_SchurComplement *)S->data;
775:   if (ainvtype) *ainvtype = schur->ainvtype;
776:   PetscFunctionReturn(PETSC_SUCCESS);
777: }

779: /*@
780:   MatCreateSchurComplementPmat - create a preconditioning matrix for the Schur complement by explicitly assembling the sparse matrix
781:   $Sp = A11 - A10 inv(DIAGFORM(A00)) A01$

783:   Collective

785:   Input Parameters:
786: + A00      - the upper-left part of the original matrix $A = [A00 A01; A10 A11]$
787: . A01      - (optional) the upper-right part of the original matrix $A = [A00 A01; A10 A11]$
788: . A10      - (optional) the lower-left part of the original matrix $A = [A00 A01; A10 A11]$
789: . A11      - (optional) the lower-right part of the original matrix $A = [A00 A01; A10 A11]$
790: . ainvtype - type of approximation for DIAGFORM(A00) used when forming $Sp = A11 - A10 inv(DIAGFORM(A00)) A01$. See `MatSchurComplementAinvType`.
791: - 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`

793:   Output Parameter:
794: . Sp - approximate Schur complement suitable for constructing a preconditioner for the true Schur complement $S = A11 - A10 inv(A00) A01$

796:   Level: advanced

798: .seealso: [](ch_ksp), `MatCreateSchurComplement()`, `MatGetSchurComplement()`, `MatSchurComplementGetPmat()`, `MatSchurComplementAinvType`
799: @*/
800: PetscErrorCode MatCreateSchurComplementPmat(Mat A00, Mat A01, Mat A10, Mat A11, MatSchurComplementAinvType ainvtype, MatReuse preuse, Mat *Sp)
801: {
802:   PetscInt N00;

804:   PetscFunctionBegin;
805:   /* 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. */
806:   /* TODO: Perhaps should create an appropriately-sized zero matrix of the same type as A00? */
808:   if (preuse == MAT_IGNORE_MATRIX) PetscFunctionReturn(PETSC_SUCCESS);

810:   /* A zero size A00 or empty A01 or A10 imply S = A11. */
811:   PetscCall(MatGetSize(A00, &N00, NULL));
812:   if (!A01 || !A10 || !N00) {
813:     if (preuse == MAT_INITIAL_MATRIX) {
814:       PetscCall(MatDuplicate(A11, MAT_COPY_VALUES, Sp));
815:     } else { /* MAT_REUSE_MATRIX */
816:       /* TODO: when can we pass SAME_NONZERO_PATTERN? */
817:       PetscCall(MatCopy(A11, *Sp, DIFFERENT_NONZERO_PATTERN));
818:     }
819:   } else {
820:     Mat       AdB, T;
821:     Vec       diag;
822:     PetscBool flg;

824:     if (ainvtype == MAT_SCHUR_COMPLEMENT_AINV_LUMP || ainvtype == MAT_SCHUR_COMPLEMENT_AINV_DIAG) {
825:       PetscCall(PetscObjectTypeCompare((PetscObject)A01, MATTRANSPOSEVIRTUAL, &flg));
826:       if (flg) {
827:         PetscCall(MatTransposeGetMat(A01, &T));
828:         PetscCall(MatTranspose(T, MAT_INITIAL_MATRIX, &AdB));
829:       } else {
830:         PetscCall(PetscObjectTypeCompare((PetscObject)A01, MATHERMITIANTRANSPOSEVIRTUAL, &flg));
831:         if (flg) {
832:           PetscCall(MatHermitianTransposeGetMat(A01, &T));
833:           PetscCall(MatHermitianTranspose(T, MAT_INITIAL_MATRIX, &AdB));
834:         }
835:       }
836:       if (!flg) PetscCall(MatDuplicate(A01, MAT_COPY_VALUES, &AdB));
837:       else {
838:         PetscScalar shift, scale;

840:         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));
841:         PetscCall(MatShift(AdB, shift));
842:         PetscCall(MatScale(AdB, scale));
843:       }
844:       PetscCall(MatCreateVecs(A00, &diag, NULL));
845:       if (ainvtype == MAT_SCHUR_COMPLEMENT_AINV_LUMP) {
846:         PetscCall(MatGetRowSum(A00, diag));
847:       } else {
848:         PetscCall(MatGetDiagonal(A00, diag));
849:       }
850:       PetscCall(VecReciprocal(diag));
851:       PetscCall(MatDiagonalScale(AdB, diag, NULL));
852:       PetscCall(VecDestroy(&diag));
853:     } else if (ainvtype == MAT_SCHUR_COMPLEMENT_AINV_BLOCK_DIAG) {
854:       Mat      A00_inv;
855:       MatType  type;
856:       MPI_Comm comm;

858:       PetscCall(PetscObjectGetComm((PetscObject)A00, &comm));
859:       PetscCall(MatGetType(A00, &type));
860:       PetscCall(MatCreate(comm, &A00_inv));
861:       PetscCall(MatSetType(A00_inv, type));
862:       PetscCall(MatInvertBlockDiagonalMat(A00, A00_inv));
863:       PetscCall(MatMatMult(A00_inv, A01, MAT_INITIAL_MATRIX, PETSC_DETERMINE, &AdB));
864:       PetscCall(MatDestroy(&A00_inv));
865:     } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unknown MatSchurComplementAinvType: %d", ainvtype);
866:     /* Cannot really reuse Sp in MatMatMult() because of MatAYPX() -->
867:          MatAXPY() --> MatHeaderReplace() --> MatDestroy_XXX_MatMatMult()  */
868:     if (preuse == MAT_REUSE_MATRIX) PetscCall(MatDestroy(Sp));
869:     PetscCall(MatMatMult(A10, AdB, MAT_INITIAL_MATRIX, PETSC_DETERMINE, Sp));
870:     PetscCall(MatScale(*Sp, -1.0));
871:     if (A11) { /* TODO: when can we pass SAME_NONZERO_PATTERN? */
872:       PetscCall(MatAXPY(*Sp, 1.0, A11, DIFFERENT_NONZERO_PATTERN));
873:     }
874:     PetscCall(MatDestroy(&AdB));
875:   }
876:   PetscFunctionReturn(PETSC_SUCCESS);
877: }

879: static PetscErrorCode MatSchurComplementGetPmat_Basic(Mat S, MatReuse preuse, Mat *Sp)
880: {
881:   Mat                  A, B, C, D;
882:   Mat_SchurComplement *schur = (Mat_SchurComplement *)S->data;
883:   MatNullSpace         sp;

885:   PetscFunctionBegin;
886:   if (preuse == MAT_IGNORE_MATRIX) PetscFunctionReturn(PETSC_SUCCESS);
887:   PetscCall(MatSchurComplementGetSubMatrices(S, &A, NULL, &B, &C, &D));
888:   PetscCheck(A, PetscObjectComm((PetscObject)S), PETSC_ERR_ARG_WRONGSTATE, "Schur complement component matrices unset");
889:   if (schur->ainvtype != MAT_SCHUR_COMPLEMENT_AINV_FULL) PetscCall(MatCreateSchurComplementPmat(A, B, C, D, schur->ainvtype, preuse, Sp));
890:   else {
891:     if (preuse == MAT_REUSE_MATRIX) PetscCall(MatDestroy(Sp));
892:     PetscCall(MatSchurComplementComputeExplicitOperator(S, Sp));
893:   }
894:   /* If the Schur complement has a nullspace, then Sp nullspace contains it, independently of the ainv type */
895:   PetscCall(MatGetNullSpace(S, &sp));
896:   if (sp) PetscCall(MatSetNullSpace(*Sp, sp));
897:   PetscCall(MatGetTransposeNullSpace(S, &sp));
898:   if (sp) PetscCall(MatSetTransposeNullSpace(*Sp, sp));
899:   PetscFunctionReturn(PETSC_SUCCESS);
900: }

902: /*@
903:   MatSchurComplementGetPmat - Obtain a preconditioning matrix for the Schur complement by assembling $Sp = A11 - A10 inv(DIAGFORM(A00)) A01$

905:   Collective

907:   Input Parameters:
908: + S      - matrix obtained with MatCreateSchurComplement() (or equivalent) that implements the action of $A11 - A10 ksp(A00,Ap00) A01$
909: - 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`

911:   Output Parameter:
912: . Sp - approximate Schur complement suitable for preconditioning the exact Schur complement $S = A11 - A10 inv(A00) A01$

914:   Level: advanced

916:   Notes:
917:   The approximation of `Sp` depends on the argument passed to `MatSchurComplementSetAinvType()`
918:   `MAT_SCHUR_COMPLEMENT_AINV_DIAG`, `MAT_SCHUR_COMPLEMENT_AINV_LUMP`, `MAT_SCHUR_COMPLEMENT_AINV_BLOCK_DIAG`, or `MAT_SCHUR_COMPLEMENT_AINV_FULL`
919:   -mat_schur_complement_ainv_type <diag,lump,blockdiag,full>

921:   Sometimes users would like to provide problem-specific data in the Schur complement, usually only
922:   for special row and column index sets.  In that case, the user should call `PetscObjectComposeFunction()` to set
923:   "MatSchurComplementGetPmat_C" to their function.  If their function needs to fall back to the default implementation,
924:   it should call `MatSchurComplementGetPmat_Basic()`.

926:   Developer Notes:
927:   The API that includes `MatGetSchurComplement()`, `MatCreateSchurComplement()`, `MatSchurComplementGetPmat()` should be refactored to
928:   remove redundancy and be clearer and simpler.

930:   This routine should be called `MatSchurComplementCreatePmat()`

932: .seealso: [](ch_ksp), `MatCreateSubMatrix()`, `PCFIELDSPLIT`, `MatGetSchurComplement()`, `MatCreateSchurComplement()`, `MatSchurComplementSetAinvType()`
933: @*/
934: PetscErrorCode MatSchurComplementGetPmat(Mat S, MatReuse preuse, Mat *Sp)
935: {
936:   PetscErrorCode (*f)(Mat, MatReuse, Mat *);

938:   PetscFunctionBegin;
942:   if (preuse != MAT_IGNORE_MATRIX) {
943:     PetscAssertPointer(Sp, 3);
944:     if (preuse == MAT_INITIAL_MATRIX) *Sp = NULL;
946:   }
947:   PetscCheck(!S->factortype, PetscObjectComm((PetscObject)S), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix");

949:   PetscCall(PetscObjectQueryFunction((PetscObject)S, "MatSchurComplementGetPmat_C", &f));
950:   if (f) PetscCall((*f)(S, preuse, Sp));
951:   else PetscCall(MatSchurComplementGetPmat_Basic(S, preuse, Sp));
952:   PetscFunctionReturn(PETSC_SUCCESS);
953: }

955: static PetscErrorCode MatProductNumeric_SchurComplement_Dense(Mat C)
956: {
957:   Mat_Product         *product = C->product;
958:   Mat_SchurComplement *Na      = (Mat_SchurComplement *)product->A->data;
959:   Mat                  work1, work2;
960:   PetscScalar         *v;
961:   PetscInt             lda;

963:   PetscFunctionBegin;
964:   PetscCall(MatMatMult(Na->B, product->B, MAT_INITIAL_MATRIX, PETSC_DETERMINE, &work1));
965:   PetscCall(MatDuplicate(work1, MAT_DO_NOT_COPY_VALUES, &work2));
966:   PetscCall(KSPMatSolve(Na->ksp, work1, work2));
967:   PetscCall(MatDestroy(&work1));
968:   PetscCall(MatDenseGetArrayWrite(C, &v));
969:   PetscCall(MatDenseGetLDA(C, &lda));
970:   PetscCall(MatCreateDense(PetscObjectComm((PetscObject)C), C->rmap->n, C->cmap->n, C->rmap->N, C->cmap->N, v, &work1));
971:   PetscCall(MatDenseSetLDA(work1, lda));
972:   PetscCall(MatMatMult(Na->C, work2, MAT_REUSE_MATRIX, PETSC_DETERMINE, &work1));
973:   PetscCall(MatDenseRestoreArrayWrite(C, &v));
974:   PetscCall(MatDestroy(&work2));
975:   PetscCall(MatDestroy(&work1));
976:   if (Na->D) {
977:     PetscCall(MatMatMult(Na->D, product->B, MAT_INITIAL_MATRIX, PETSC_DETERMINE, &work1));
978:     PetscCall(MatAYPX(C, -1.0, work1, SAME_NONZERO_PATTERN));
979:     PetscCall(MatDestroy(&work1));
980:   } else PetscCall(MatScale(C, -1.0));
981:   PetscFunctionReturn(PETSC_SUCCESS);
982: }

984: static PetscErrorCode MatProductSymbolic_SchurComplement_Dense(Mat C)
985: {
986:   Mat_Product *product = C->product;
987:   Mat          A = product->A, B = product->B;
988:   PetscInt     m = A->rmap->n, n = B->cmap->n, M = A->rmap->N, N = B->cmap->N;
989:   PetscBool    flg;

991:   PetscFunctionBegin;
992:   PetscCall(MatSetSizes(C, m, n, M, N));
993:   PetscCall(PetscObjectBaseTypeCompareAny((PetscObject)C, &flg, MATSEQDENSE, MATMPIDENSE, ""));
994:   if (!flg) {
995:     PetscCall(MatSetType(C, ((PetscObject)B)->type_name));
996:     C->ops->productsymbolic = MatProductSymbolic_SchurComplement_Dense;
997:   }
998:   PetscCall(MatSetUp(C));
999:   C->ops->productnumeric = MatProductNumeric_SchurComplement_Dense;
1000:   PetscFunctionReturn(PETSC_SUCCESS);
1001: }

1003: static PetscErrorCode MatProductSetFromOptions_SchurComplement_Dense(Mat C)
1004: {
1005:   Mat_Product *product = C->product;

1007:   PetscFunctionBegin;
1008:   if (product->type != MATPRODUCT_AB) PetscFunctionReturn(PETSC_SUCCESS);
1009:   C->ops->productsymbolic = MatProductSymbolic_SchurComplement_Dense;
1010:   PetscFunctionReturn(PETSC_SUCCESS);
1011: }

1013: static PetscErrorCode MatProductNumeric_SchurComplement_Any(Mat C)
1014: {
1015:   Mat_SchurComplement *Na = (Mat_SchurComplement *)C->data;

1017:   PetscFunctionBegin;
1018:   if (Na->D && Na->D->product) PetscCall(MatProductNumeric(Na->D));
1019:   if (Na->B->product) PetscCall(MatProductNumeric(Na->B));
1020:   if (Na->C->product) PetscCall(MatProductNumeric(Na->C));
1021:   C->assembled = PETSC_TRUE;
1022:   PetscFunctionReturn(PETSC_SUCCESS);
1023: }

1025: static PetscErrorCode MatProductSymbolic_SchurComplement_Any(Mat C)
1026: {
1027:   Mat_SchurComplement *Na = (Mat_SchurComplement *)C->data;

1029:   PetscFunctionBegin;
1030:   if (Na->D && Na->D->product) PetscCall(MatProductSymbolic(Na->D));
1031:   if (Na->B->product) PetscCall(MatProductSymbolic(Na->B));
1032:   if (Na->C->product) PetscCall(MatProductSymbolic(Na->C));
1033:   C->ops->productnumeric = MatProductNumeric_SchurComplement_Any;
1034:   C->preallocated        = PETSC_TRUE;
1035:   C->assembled           = PETSC_FALSE;
1036:   PetscFunctionReturn(PETSC_SUCCESS);
1037: }

1039: static PetscErrorCode MatProductSetFromOptions_SchurComplement_Any(Mat C)
1040: {
1041:   Mat_Product         *product = C->product;
1042:   Mat_SchurComplement *Na, *Ca;
1043:   Mat                  B = product->B, S = product->A, pB = NULL, pC = NULL, pD = NULL;
1044:   KSP                  ksp;
1045:   PetscInt             m = PETSC_DECIDE, n = PETSC_DECIDE, M = PETSC_DECIDE, N = PETSC_DECIDE;
1046:   MatProductType       pbtype = MATPRODUCT_UNSPECIFIED, pctype = MATPRODUCT_UNSPECIFIED;
1047:   PetscBool            isschur;

1049:   PetscFunctionBegin;
1050:   if (product->type == MATPRODUCT_ABC || product->type == MATPRODUCT_AtB) PetscFunctionReturn(PETSC_SUCCESS);
1051:   /* A * S not yet supported (should be easy though) */
1052:   PetscCall(PetscObjectTypeCompare((PetscObject)S, MATSCHURCOMPLEMENT, &isschur));
1053:   if (!isschur) PetscFunctionReturn(PETSC_SUCCESS);

1055:   Na = (Mat_SchurComplement *)S->data;
1056:   if (Na->D) {
1057:     PetscCall(MatProductCreate(Na->D, B, NULL, &pD));
1058:     PetscCall(MatProductSetType(pD, product->type));
1059:     PetscCall(MatProductSetFromOptions(pD));
1060:   }
1061:   if (pD && !pD->ops->productsymbolic) {
1062:     PetscCall(MatDestroy(&pD));
1063:     PetscFunctionReturn(PETSC_SUCCESS);
1064:   }

1066:   /* S = A11 - A10 M A01 */
1067:   switch (product->type) {
1068:   case MATPRODUCT_AB: /* A11 B - A10 * M * A01 * B */
1069:     pbtype = product->type;
1070:     PetscCall(PetscObjectReference((PetscObject)Na->C));
1071:     pC = Na->C;
1072:     m  = S->rmap->n;
1073:     M  = S->rmap->N;
1074:     n  = B->cmap->n;
1075:     N  = B->cmap->N;
1076:     break;
1077:   case MATPRODUCT_ABt: /* A11 B^t - A10 * M * A01 * B^t */
1078:     pbtype = product->type;
1079:     PetscCall(PetscObjectReference((PetscObject)Na->C));
1080:     pC = Na->C;
1081:     m  = S->rmap->n;
1082:     M  = S->rmap->N;
1083:     n  = B->rmap->n;
1084:     N  = B->rmap->N;
1085:     break;
1086:   case MATPRODUCT_PtAP: /* Pt A11 P - Pt * A10 * M * A01 * P */
1087:     pbtype = MATPRODUCT_AB;
1088:     pctype = MATPRODUCT_AtB;
1089:     m      = B->cmap->n;
1090:     M      = B->cmap->N;
1091:     n      = B->cmap->n;
1092:     N      = B->cmap->N;
1093:     break;
1094:   case MATPRODUCT_RARt: /* R A11 Rt - R * A10 * M * A01 * Rt */
1095:     pbtype = MATPRODUCT_ABt;
1096:     pctype = MATPRODUCT_AB;
1097:     m      = B->rmap->n;
1098:     M      = B->rmap->N;
1099:     n      = B->rmap->n;
1100:     N      = B->rmap->N;
1101:     break;
1102:   default:
1103:     break;
1104:   }
1105:   PetscCall(MatProductCreate(Na->B, B, NULL, &pB));
1106:   PetscCall(MatProductSetType(pB, pbtype));
1107:   PetscCall(MatProductSetFromOptions(pB));
1108:   if (!pB->ops->productsymbolic) {
1109:     PetscCall(MatDestroy(&pB));
1110:     PetscFunctionReturn(PETSC_SUCCESS);
1111:   }
1112:   if (pC == NULL) { /* Some work can in principle be saved here if we recognize symmetry */
1113:     PetscCall(MatProductCreate(B, Na->C, NULL, &pC));
1114:     PetscCall(MatProductSetType(pC, pctype));
1115:     PetscCall(MatProductSetFromOptions(pC));
1116:     if (!pC->ops->productsymbolic) {
1117:       PetscCall(MatDestroy(&pC));
1118:       PetscFunctionReturn(PETSC_SUCCESS);
1119:     }
1120:   }
1121:   PetscCall(MatSetType(C, MATSCHURCOMPLEMENT));
1122:   PetscCall(MatSetSizes(C, m, n, M, N));
1123:   PetscCall(PetscLayoutSetUp(C->rmap));
1124:   PetscCall(PetscLayoutSetUp(C->cmap));
1125:   PetscCall(PetscObjectReference((PetscObject)Na->A));
1126:   PetscCall(PetscObjectReference((PetscObject)Na->Ap));
1127:   Ca                      = (Mat_SchurComplement *)C->data;
1128:   Ca->A                   = Na->A;
1129:   Ca->Ap                  = Na->Ap;
1130:   Ca->B                   = pB;
1131:   Ca->C                   = pC;
1132:   Ca->D                   = pD;
1133:   C->ops->productsymbolic = MatProductSymbolic_SchurComplement_Any;
1134:   PetscCall(MatSchurComplementGetKSP(S, &ksp));
1135:   PetscCall(MatSchurComplementSetKSP(C, ksp));
1136:   PetscFunctionReturn(PETSC_SUCCESS);
1137: }

1139: /*MC
1140:   MATSCHURCOMPLEMENT -  "schurcomplement" - Matrix type that behaves like the Schur complement of a matrix.

1142:   Level: intermediate

1144: .seealso: [](ch_matrices), `Mat`, `MatCreate()`, `MatType`, `MatCreateSchurComplement()`, `MatSchurComplementComputeExplicitOperator()`,
1145:           `MatSchurComplementGetSubMatrices()`, `MatSchurComplementGetKSP()`
1146: M*/
1147: PETSC_EXTERN PetscErrorCode MatCreate_SchurComplement(Mat N)
1148: {
1149:   Mat_SchurComplement *Na;

1151:   PetscFunctionBegin;
1152:   PetscCall(PetscNew(&Na));
1153:   N->data = (void *)Na;

1155:   N->ops->destroy        = MatDestroy_SchurComplement;
1156:   N->ops->getvecs        = MatCreateVecs_SchurComplement;
1157:   N->ops->view           = MatView_SchurComplement;
1158:   N->ops->mult           = MatMult_SchurComplement;
1159:   N->ops->multtranspose  = MatMultTranspose_SchurComplement;
1160:   N->ops->multadd        = MatMultAdd_SchurComplement;
1161:   N->ops->setfromoptions = MatSetFromOptions_SchurComplement;
1162:   N->assembled           = PETSC_FALSE;
1163:   N->preallocated        = PETSC_FALSE;

1165:   PetscCall(KSPCreate(PetscObjectComm((PetscObject)N), &Na->ksp));
1166:   PetscCall(PetscObjectChangeTypeName((PetscObject)N, MATSCHURCOMPLEMENT));
1167:   PetscCall(PetscObjectComposeFunction((PetscObject)N, "MatProductSetFromOptions_schurcomplement_seqdense_C", MatProductSetFromOptions_SchurComplement_Dense));
1168:   PetscCall(PetscObjectComposeFunction((PetscObject)N, "MatProductSetFromOptions_schurcomplement_mpidense_C", MatProductSetFromOptions_SchurComplement_Dense));
1169:   PetscCall(PetscObjectComposeFunction((PetscObject)N, "MatProductSetFromOptions_anytype_C", MatProductSetFromOptions_SchurComplement_Any));
1170:   PetscFunctionReturn(PETSC_SUCCESS);
1171: }