xref: /petsc/src/snes/mf/snesmfj.c (revision feff33ee0b5b037fa8f9f294dede656a2f85cc47)
1 
2 #include <petsc/private/snesimpl.h>  /*I  "petscsnes.h" I*/
3 #include <petscdm.h>                 /*I  "petscdm.h"   I*/
4 #include <../src/mat/impls/mffd/mffdimpl.h>
5 #include <petsc/private/matimpl.h>
6 
7 /*@C
8    MatMFFDComputeJacobian - Tells the matrix-free Jacobian object the new location at which
9        Jacobian matrix vector products will be computed at, i.e. J(x) * a. The x is obtained
10        from the SNES object (using SNESGetSolution()).
11 
12    Logically Collective on SNES
13 
14    Input Parameters:
15 +   snes - the nonlinear solver context
16 .   x - the point at which the Jacobian vector products will be performed
17 .   jac - the matrix-free Jacobian object
18 .   B - either the same as jac or another matrix type (ignored)
19 .   flag - not relevent for matrix-free form
20 -   dummy - the user context (ignored)
21 
22    Level: developer
23 
24    Warning:
25       If MatMFFDSetBase() is ever called on jac then this routine will NO longer get
26     the x from the SNES object and MatMFFDSetBase() must from that point on be used to
27     change the base vector x.
28 
29    Notes:
30      This can be passed into SNESSetJacobian() as the Jacobian evaluation function argument
31      when using a completely matrix-free solver,
32      that is the B matrix is also the same matrix operator. This is used when you select
33      -snes_mf but rarely used directly by users. (All this routine does is call MatAssemblyBegin/End() on
34      the Mat jac.)
35 
36 .seealso: MatMFFDGetH(), MatCreateSNESMF(), MatCreateMFFD(), MATMFFD,
37           MatMFFDSetHHistory(), MatMFFDSetFunctionError(), MatCreateMFFD(), SNESSetJacobian()
38 
39 @*/
40 PetscErrorCode  MatMFFDComputeJacobian(SNES snes,Vec x,Mat jac,Mat B,void *dummy)
41 {
42   PetscErrorCode ierr;
43 
44   PetscFunctionBegin;
45   ierr = MatAssemblyBegin(jac,MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
46   ierr = MatAssemblyEnd(jac,MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
47   PetscFunctionReturn(0);
48 }
49 
50 PETSC_EXTERN PetscErrorCode MatAssemblyEnd_MFFD(Mat,MatAssemblyType);
51 PETSC_EXTERN PetscErrorCode MatMFFDSetBase_MFFD(Mat,Vec,Vec);
52 
53 /*@
54     MatSNESMFGetSNES - returns the SNES associated with a matrix created with MatCreateSNESMF()
55 
56     Not collective
57 
58     Input Parameter:
59 .   J - the matrix
60 
61     Output Parameter:
62 .   snes - the SNES object
63 
64 .seealso: MatCreateSNESMF()
65 @*/
66 PetscErrorCode MatSNESMFGetSNES(Mat J,SNES *snes)
67 {
68   MatMFFD        j    = (MatMFFD)J->data;
69 
70   PetscFunctionBegin;
71   *snes = (SNES)j->ctx;
72   PetscFunctionReturn(0);
73 }
74 
75 /*
76    MatAssemblyEnd_SNESMF - Calls MatAssemblyEnd_MFFD() and then sets the
77     base from the SNES context
78 
79 */
80 static PetscErrorCode MatAssemblyEnd_SNESMF(Mat J,MatAssemblyType mt)
81 {
82   PetscErrorCode ierr;
83   MatMFFD        j    = (MatMFFD)J->data;
84   SNES           snes = (SNES)j->ctx;
85   Vec            u,f;
86 
87   PetscFunctionBegin;
88   ierr = MatAssemblyEnd_MFFD(J,mt);CHKERRQ(ierr);
89 
90   ierr = SNESGetSolution(snes,&u);CHKERRQ(ierr);
91   if (j->func == (PetscErrorCode (*)(void*,Vec,Vec))SNESComputeFunction) {
92     ierr = SNESGetFunction(snes,&f,NULL,NULL);CHKERRQ(ierr);
93     ierr = MatMFFDSetBase_MFFD(J,u,f);CHKERRQ(ierr);
94   } else {
95     /* f value known by SNES is not correct for other differencing function */
96     ierr = MatMFFDSetBase_MFFD(J,u,NULL);CHKERRQ(ierr);
97   }
98   PetscFunctionReturn(0);
99 }
100 
101 /*
102    MatAssemblyEnd_SNESMF_UseBase - Calls MatAssemblyEnd_MFFD() and then sets the
103     base from the SNES context. This version will cause the base to be used for differencing
104     even if the func is not SNESComputeFunction. See: MatSNESMFUseBase()
105 
106 
107 */
108 static PetscErrorCode MatAssemblyEnd_SNESMF_UseBase(Mat J,MatAssemblyType mt)
109 {
110   PetscErrorCode ierr;
111   MatMFFD        j    = (MatMFFD)J->data;
112   SNES           snes = (SNES)j->ctx;
113   Vec            u,f;
114 
115   PetscFunctionBegin;
116   ierr = MatAssemblyEnd_MFFD(J,mt);CHKERRQ(ierr);
117 
118   ierr = SNESGetSolution(snes,&u);CHKERRQ(ierr);
119   ierr = SNESGetFunction(snes,&f,NULL,NULL);CHKERRQ(ierr);
120   ierr = MatMFFDSetBase_MFFD(J,u,f);CHKERRQ(ierr);
121   PetscFunctionReturn(0);
122 }
123 
124 /*
125     This routine resets the MatAssemblyEnd() for the MatMFFD created from MatCreateSNESMF() so that it NO longer
126   uses the solution in the SNES object to update the base. See the warning in MatCreateSNESMF().
127 */
128 static PetscErrorCode  MatMFFDSetBase_SNESMF(Mat J,Vec U,Vec F)
129 {
130   PetscErrorCode ierr;
131 
132   PetscFunctionBegin;
133   ierr = MatMFFDSetBase_MFFD(J,U,F);CHKERRQ(ierr);
134   J->ops->assemblyend = MatAssemblyEnd_MFFD;
135   PetscFunctionReturn(0);
136 }
137 
138 static PetscErrorCode  MatSNESMFSetReuseBase_SNESMF(Mat J,PetscBool use)
139 {
140   PetscFunctionBegin;
141   if (use) {
142     J->ops->assemblyend = MatAssemblyEnd_SNESMF_UseBase;
143   } else {
144     J->ops->assemblyend = MatAssemblyEnd_SNESMF;
145   }
146   PetscFunctionReturn(0);
147 }
148 
149 /*@
150     MatSNESMFSetReuseBase - Causes the base vector to be used for differencing even if the function provided to SNESSetFunction() is not the
151                        same as that provided to MatMFFDSetFunction().
152 
153     Logically Collective on Mat
154 
155     Input Parameters:
156 +   J - the MatMFFD matrix
157 -   use - if true always reuse the base vector instead of recomputing f(u) even if the function in the MatSNESMF is
158           not SNESComputeFunction()
159 
160     Notes: Care must be taken when using this routine to insure that the function provided to MatMFFDSetFunction(), call it F_MF() is compatible with
161            with that provided to SNESSetFunction(), call it F_SNES(). That is, (F_MF(u + h*d) - F_SNES(u))/h has to approximate the derivative
162 
163     Developer Notes: This was provided for the MOOSE team who desired to have a SNESSetFunction() function that could change configurations due
164                      to contacts while the function provided to MatMFFDSetFunction() cannot. Except for the possibility of changing the configuration
165                      both functions compute the same mathematical function so the differencing makes sense.
166 
167     Level: advanced
168 
169 .seealso: MatCreateSNESMF(), MatSNESMFGetReuseBase()
170 @*/
171 PetscErrorCode  MatSNESMFSetReuseBase(Mat J,PetscBool use)
172 {
173   PetscErrorCode ierr;
174 
175   PetscFunctionBegin;
176   PetscValidHeaderSpecific(J,MAT_CLASSID,1);
177   ierr = PetscTryMethod(J,"MatSNESMFSetReuseBase_C",(Mat,PetscBool),(J,use));CHKERRQ(ierr);
178   PetscFunctionReturn(0);
179 }
180 
181 static PetscErrorCode  MatSNESMFGetReuseBase_SNESMF(Mat J,PetscBool *use)
182 {
183   PetscFunctionBegin;
184   if (J->ops->assemblyend == MatAssemblyEnd_SNESMF_UseBase) *use = PETSC_TRUE;
185   else *use = PETSC_FALSE;
186   PetscFunctionReturn(0);
187 }
188 
189 /*@
190     MatSNESMFGetReuseBase - Determines if the base vector is to be used for differencing even if the function provided to SNESSetFunction() is not the
191                        same as that provided to MatMFFDSetFunction().
192 
193     Logically Collective on Mat
194 
195     Input Parameter:
196 .   J - the MatMFFD matrix
197 
198     Output Parameter:
199 .   use - if true always reuse the base vector instead of recomputing f(u) even if the function in the MatSNESMF is
200           not SNESComputeFunction()
201 
202     Notes: Care must be taken when using this routine to insure that the function provided to MatMFFDSetFunction(), call it F_MF() is compatible with
203            with that provided to SNESSetFunction(), call it F_SNES(). That is, (F_MF(u + h*d) - F_SNES(u))/h has to approximate the derivative
204 
205     Developer Notes: This was provided for the MOOSE team who desired to have a SNESSetFunction() function that could change configurations due
206                      to contacts while the function provided to MatMFFDSetFunction() cannot. Except for the possibility of changing the configuration
207                      both functions compute the same mathematical function so the differencing makes sense.
208 
209     Level: advanced
210 
211 .seealso: MatCreateSNESMF(), MatSNESMFSetReuseBase()
212 @*/
213 PetscErrorCode  MatSNESMFGetReuseBase(Mat J,PetscBool *use)
214 {
215   PetscErrorCode ierr;
216 
217   PetscFunctionBegin;
218   PetscValidHeaderSpecific(J,MAT_CLASSID,1);
219   ierr = PetscUseMethod(J,"MatSNESMFGetReuseBase_C",(Mat,PetscBool*),(J,use));CHKERRQ(ierr);
220   PetscFunctionReturn(0);
221 }
222 
223 /*@
224    MatCreateSNESMF - Creates a matrix-free matrix context for use with
225    a SNES solver.  This matrix can be used as the Jacobian argument for
226    the routine SNESSetJacobian(). See MatCreateMFFD() for details on how
227    the finite difference computation is done.
228 
229    Collective on SNES and Vec
230 
231    Input Parameters:
232 .  snes - the SNES context
233 
234    Output Parameter:
235 .  J - the matrix-free matrix
236 
237    Level: advanced
238 
239 
240    Notes:
241      You can call SNESSetJacobian() with MatMFFDComputeJacobian() if you are using matrix and not a different
242      preconditioner matrix
243 
244      If you wish to provide a different function to do differencing on to compute the matrix-free operator than
245      that provided to SNESSetFunction() then call MatMFFDSetFunction() with your function after this call.
246 
247      The difference between this routine and MatCreateMFFD() is that this matrix
248      automatically gets the current base vector from the SNES object and not from an
249      explicit call to MatMFFDSetBase().
250 
251    Warning:
252      If MatMFFDSetBase() is ever called on jac then this routine will NO longer get
253      the x from the SNES object and MatMFFDSetBase() must from that point on be used to
254      change the base vector x.
255 
256    Warning:
257      Using a different function for the differencing will not work if you are using non-linear left preconditioning.
258 
259 
260 .seealso: MatDestroy(), MatMFFDSetFunction(), MatMFFDSetFunctionError(), MatMFFDDSSetUmin()
261           MatMFFDSetHHistory(), MatMFFDResetHHistory(), MatCreateMFFD(),
262           MatMFFDGetH(), MatMFFDRegister(), MatMFFDComputeJacobian(), MatSNESMFSetReuseBase(), MatSNESMFGetReuseBase()
263 
264 @*/
265 PetscErrorCode  MatCreateSNESMF(SNES snes,Mat *J)
266 {
267   PetscErrorCode ierr;
268   PetscInt       n,N;
269   MatMFFD        mf;
270 
271   PetscFunctionBegin;
272   if (snes->vec_func) {
273     ierr = VecGetLocalSize(snes->vec_func,&n);CHKERRQ(ierr);
274     ierr = VecGetSize(snes->vec_func,&N);CHKERRQ(ierr);
275   } else if (snes->dm) {
276     Vec tmp;
277     ierr = DMGetGlobalVector(snes->dm,&tmp);CHKERRQ(ierr);
278     ierr = VecGetLocalSize(tmp,&n);CHKERRQ(ierr);
279     ierr = VecGetSize(tmp,&N);CHKERRQ(ierr);
280     ierr = DMRestoreGlobalVector(snes->dm,&tmp);CHKERRQ(ierr);
281   } else SETERRQ(PetscObjectComm((PetscObject)snes),PETSC_ERR_ARG_WRONGSTATE,"Must call SNESSetFunction() or SNESSetDM() first");
282   ierr = MatCreateMFFD(PetscObjectComm((PetscObject)snes),n,n,N,N,J);CHKERRQ(ierr);
283 
284   mf      = (MatMFFD)(*J)->data;
285   mf->ctx = snes;
286 
287   if (snes->npc && snes->npcside== PC_LEFT) {
288     ierr = MatMFFDSetFunction(*J,(PetscErrorCode (*)(void*,Vec,Vec))SNESComputeFunctionDefaultNPC,snes);CHKERRQ(ierr);
289   } else {
290     ierr = MatMFFDSetFunction(*J,(PetscErrorCode (*)(void*,Vec,Vec))SNESComputeFunction,snes);CHKERRQ(ierr);
291   }
292 
293   (*J)->ops->assemblyend = MatAssemblyEnd_SNESMF;
294 
295   ierr = PetscObjectComposeFunction((PetscObject)*J,"MatMFFDSetBase_C",MatMFFDSetBase_SNESMF);CHKERRQ(ierr);
296   ierr = PetscObjectComposeFunction((PetscObject)*J,"MatSNESMFSetReuseBase_C",MatSNESMFSetReuseBase_SNESMF);CHKERRQ(ierr);
297   ierr = PetscObjectComposeFunction((PetscObject)*J,"MatSNESMFGetReuseBase_C",MatSNESMFGetReuseBase_SNESMF);CHKERRQ(ierr);
298   PetscFunctionReturn(0);
299 }
300