Actual source code: lsc.c
1: #define PETSCKSP_DLL
4: #include private/pcimpl.h
6: typedef struct {
7: PetscTruth allocated;
8: PetscTruth scalediag;
9: KSP kspL;
10: Vec scale;
11: Vec x0,y0,x1;
12: } PC_LSC;
16: static PetscErrorCode PCLSCAllocate_Private(PC pc)
17: {
18: PC_LSC *lsc = (PC_LSC*)pc->data;
19: Mat A;
20: PetscErrorCode ierr;
23: if (lsc->allocated) return(0);
24: KSPCreate(((PetscObject)pc)->comm,&lsc->kspL);
25: KSPSetType(lsc->kspL,KSPPREONLY);
26: KSPSetOptionsPrefix(lsc->kspL,((PetscObject)pc)->prefix);
27: KSPAppendOptionsPrefix(lsc->kspL,"lsc_");
28: KSPSetFromOptions(lsc->kspL);
29: MatSchurComplementGetSubmatrices(pc->mat,&A,PETSC_NULL,PETSC_NULL,PETSC_NULL,PETSC_NULL);
30: MatGetVecs(A,&lsc->x0,&lsc->y0);
31: MatGetVecs(pc->pmat,&lsc->x1,PETSC_NULL);
32: if (lsc->scalediag) {
33: VecDuplicate(lsc->x0,&lsc->scale);
34: }
35: lsc->allocated = PETSC_TRUE;
36: return(0);
37: }
41: static PetscErrorCode PCSetUp_LSC(PC pc)
42: {
43: PC_LSC *lsc = (PC_LSC*)pc->data;
44: Mat L,Lp;
45: PetscErrorCode ierr;
48: PCLSCAllocate_Private(pc);
49: PetscObjectQuery((PetscObject)pc->pmat,"LSC_L",(PetscObject*)&L);
50: PetscObjectQuery((PetscObject)pc->pmat,"LSC_Lp",(PetscObject*)&Lp);
51: if (lsc->scale) {
52: Mat Ap;
53: MatSchurComplementGetSubmatrices(pc->mat,PETSC_NULL,&Ap,PETSC_NULL,PETSC_NULL,PETSC_NULL);
54: MatGetDiagonal(Ap,lsc->scale); /* Should be the mass matrix, but we don't have plumbing for that yet */
55: VecReciprocal(lsc->scale);
56: }
57: KSPSetOperators(lsc->kspL,L,Lp,SAME_NONZERO_PATTERN);
58: return(0);
59: }
63: static PetscErrorCode PCApply_LSC(PC pc,Vec x,Vec y)
64: {
65: PC_LSC *lsc = (PC_LSC*)pc->data;
66: Mat A,B,C;
70: MatSchurComplementGetSubmatrices(pc->mat,&A,PETSC_NULL,&B,&C,PETSC_NULL);
71: KSPSolve(lsc->kspL,x,lsc->x1);
72: MatMult(B,lsc->x1,lsc->x0);
73: if (lsc->scale) {
74: VecPointwiseMult(lsc->x0,lsc->x0,lsc->scale);
75: }
76: MatMult(A,lsc->x0,lsc->y0);
77: if (lsc->scale) {
78: VecPointwiseMult(lsc->y0,lsc->y0,lsc->scale);
79: }
80: MatMult(C,lsc->y0,lsc->x1);
81: KSPSolve(lsc->kspL,lsc->x1,y);
82: return(0);
83: }
87: static PetscErrorCode PCDestroy_LSC(PC pc)
88: {
89: PC_LSC *lsc = (PC_LSC*)pc->data;
93: if (lsc->x0) {VecDestroy(lsc->x0);}
94: if (lsc->y0) {VecDestroy(lsc->y0);}
95: if (lsc->x1) {VecDestroy(lsc->x1);}
96: if (lsc->scale) {VecDestroy(lsc->scale);}
97: if (lsc->kspL) {KSPDestroy(lsc->kspL);}
98: PetscFree(lsc);
99: return(0);
100: }
104: static PetscErrorCode PCSetFromOptions_LSC(PC pc)
105: {
106: PC_LSC *lsc = (PC_LSC*)pc->data;
107: PetscErrorCode ierr;
110: PetscOptionsHead("LSC options");
111: {
112: PetscOptionsTruth("-pc_lsc_scale_diag","Use diagonal of velocity block (A) for scaling","None",lsc->scalediag,&lsc->scalediag,PETSC_NULL);
113: }
114: PetscOptionsTail();
115: return(0);
116: }
118: /*MC
119: PCLSC - Preconditioning for Schur complements, based on Least Squares Commutators
121: Options Database Key:
122: . -pc_lsc_scale_diag - Use the diagonal of A for scaling
124: Level: intermediate
126: Notes:
127: This preconditioner will normally be used with PCFieldSplit to precondition the Schur complement, but
128: it can be used for any Schur complement system. Consider the Schur complement
130: .vb
131: S = D - C inv(A) B
132: .ve
134: PCLSC currently doesn't do anything with D, so let's assume it is 0. The idea is that a good approximation to
135: inv(S) is given by
137: .vb
138: inv(CB) C A B inv(CB)
139: .ve
141: At some point, we'll be able to form the product CB for you, but for now the application has to provide it (this is
142: usually more efficient anyway). In the case of incompressible flow, CB is a Laplacian, call it L. The current
143: interface is to hang L and a preconditioning matrix Lp on the preconditioning matrix.
145: If you had called KSPSetOperators(ksp,S,Sp,flg), S should have type MATSCHURCOMPLEMENT and Sp can be any type you
146: like (PCLSC doesn't use it directly) but should have matrices composed with it, under the names "LSC_L" and "LSC_Lp".
147: For example, you might have setup code like this
149: .vb
150: PetscObjectCompose((PetscObject)Sp,"LSC_L",(PetscObject)L);
151: PetscObjectCompose((PetscObject)Sp,"LSC_Lp",(PetscObject)Lp);
152: .ve
154: And then your Jacobian assembly would look like
156: .vb
157: PetscObjectQuery((PetscObject)Sp,"LSC_L",(PetscObject*)&L);
158: PetscObjectQuery((PetscObject)Sp,"LSC_Lp",(PetscObject*)&Lp);
159: if (L) { assembly L }
160: if (Lp) { assemble Lp }
161: .ve
163: With this, you should be able to choose LSC preconditioning, using e.g. ML's algebraic multigrid to solve with L
165: .vb
166: -fieldsplit_1_pc_type lsc -fieldsplit_1_lsc_pc_type ml
167: .ve
169: Since we do not use the values in Sp, you can still put an assembled matrix there to use normal preconditioners.
171: Concepts: physics based preconditioners, block preconditioners
173: .seealso: PCCreate(), PCSetType(), PCType (for list of available types), PC, Block_Preconditioners, PCFIELDSPLIT,
174: PCFieldSplitGetSubKSP(), PCFieldSplitSetFields(), PCFieldSplitSetType(), PCFieldSplitSetIS(), PCFieldSplitSchurPrecondition(),
175: MatCreateSchurComplement()
176: M*/
181: PetscErrorCode PCCreate_LSC(PC pc)
182: {
183: PC_LSC *lsc;
187: PetscNewLog(pc,PC_LSC,&lsc);
188: pc->data = (void*)lsc;
190: pc->ops->apply = PCApply_LSC;
191: pc->ops->applytranspose = 0;
192: pc->ops->setup = PCSetUp_LSC;
193: pc->ops->destroy = PCDestroy_LSC;
194: pc->ops->setfromoptions = PCSetFromOptions_LSC;
195: pc->ops->view = 0;
196: pc->ops->applyrichardson = 0;
197: return(0);
198: }