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 relevant 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 PetscFunctionBegin; 42 PetscCall(MatAssemblyBegin(jac, MAT_FINAL_ASSEMBLY)); 43 PetscCall(MatAssemblyEnd(jac, MAT_FINAL_ASSEMBLY)); 44 PetscFunctionReturn(0); 45 } 46 47 PETSC_EXTERN PetscErrorCode MatAssemblyEnd_MFFD(Mat, MatAssemblyType); 48 PETSC_EXTERN PetscErrorCode MatMFFDSetBase_MFFD(Mat, Vec, Vec); 49 50 /*@ 51 MatSNESMFGetSNES - returns the SNES associated with a matrix created with MatCreateSNESMF() 52 53 Not collective 54 55 Input Parameter: 56 . J - the matrix 57 58 Output Parameter: 59 . snes - the SNES object 60 61 Level: advanced 62 63 .seealso: `MatCreateSNESMF()` 64 @*/ 65 PetscErrorCode MatSNESMFGetSNES(Mat J, SNES *snes) { 66 MatMFFD j; 67 68 PetscFunctionBegin; 69 PetscCall(MatShellGetContext(J, &j)); 70 *snes = (SNES)j->ctx; 71 PetscFunctionReturn(0); 72 } 73 74 /* 75 MatAssemblyEnd_SNESMF - Calls MatAssemblyEnd_MFFD() and then sets the 76 base from the SNES context 77 78 */ 79 static PetscErrorCode MatAssemblyEnd_SNESMF(Mat J, MatAssemblyType mt) { 80 MatMFFD j; 81 SNES snes; 82 Vec u, f; 83 DM dm; 84 DMSNES dms; 85 86 PetscFunctionBegin; 87 PetscCall(MatShellGetContext(J, &j)); 88 snes = (SNES)j->ctx; 89 PetscCall(MatAssemblyEnd_MFFD(J, mt)); 90 91 PetscCall(SNESGetSolution(snes, &u)); 92 PetscCall(SNESGetDM(snes, &dm)); 93 PetscCall(DMGetDMSNES(dm, &dms)); 94 if ((j->func == (PetscErrorCode(*)(void *, Vec, Vec))SNESComputeFunction) && !dms->ops->computemffunction) { 95 PetscCall(SNESGetFunction(snes, &f, NULL, NULL)); 96 PetscCall(MatMFFDSetBase_MFFD(J, u, f)); 97 } else { 98 /* f value known by SNES is not correct for other differencing function */ 99 PetscCall(MatMFFDSetBase_MFFD(J, u, NULL)); 100 } 101 PetscFunctionReturn(0); 102 } 103 104 /* 105 MatAssemblyEnd_SNESMF_UseBase - Calls MatAssemblyEnd_MFFD() and then sets the 106 base from the SNES context. This version will cause the base to be used for differencing 107 even if the func is not SNESComputeFunction. See: MatSNESMFUseBase() 108 109 */ 110 static PetscErrorCode MatAssemblyEnd_SNESMF_UseBase(Mat J, MatAssemblyType mt) { 111 MatMFFD j; 112 SNES snes; 113 Vec u, f; 114 115 PetscFunctionBegin; 116 PetscCall(MatAssemblyEnd_MFFD(J, mt)); 117 PetscCall(MatShellGetContext(J, &j)); 118 snes = (SNES)j->ctx; 119 PetscCall(SNESGetSolution(snes, &u)); 120 PetscCall(SNESGetFunction(snes, &f, NULL, NULL)); 121 PetscCall(MatMFFDSetBase_MFFD(J, u, f)); 122 PetscFunctionReturn(0); 123 } 124 125 /* 126 This routine resets the MatAssemblyEnd() for the MatMFFD created from MatCreateSNESMF() so that it NO longer 127 uses the solution in the SNES object to update the base. See the warning in MatCreateSNESMF(). 128 */ 129 static PetscErrorCode MatMFFDSetBase_SNESMF(Mat J, Vec U, Vec F) { 130 PetscFunctionBegin; 131 PetscCall(MatMFFDSetBase_MFFD(J, U, F)); 132 J->ops->assemblyend = MatAssemblyEnd_MFFD; 133 PetscFunctionReturn(0); 134 } 135 136 static PetscErrorCode MatSNESMFSetReuseBase_SNESMF(Mat J, PetscBool use) { 137 PetscFunctionBegin; 138 if (use) { 139 J->ops->assemblyend = MatAssemblyEnd_SNESMF_UseBase; 140 } else { 141 J->ops->assemblyend = MatAssemblyEnd_SNESMF; 142 } 143 PetscFunctionReturn(0); 144 } 145 146 /*@ 147 MatSNESMFSetReuseBase - Causes the base vector to be used for differencing even if the function provided to SNESSetFunction() is not the 148 same as that provided to MatMFFDSetFunction(). 149 150 Logically Collective on Mat 151 152 Input Parameters: 153 + J - the MatMFFD matrix 154 - use - if true always reuse the base vector instead of recomputing f(u) even if the function in the MatSNESMF is 155 not SNESComputeFunction() 156 157 Notes: 158 Care must be taken when using this routine to insure that the function provided to MatMFFDSetFunction(), call it F_MF() is compatible with 159 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 160 161 Developer Notes: 162 This was provided for the MOOSE team who desired to have a SNESSetFunction() function that could change configurations due 163 to contacts while the function provided to MatMFFDSetFunction() cannot. Except for the possibility of changing the configuration 164 both functions compute the same mathematical function so the differencing makes sense. 165 166 Level: advanced 167 168 .seealso: `MatCreateSNESMF()`, `MatSNESMFGetReuseBase()` 169 @*/ 170 PetscErrorCode MatSNESMFSetReuseBase(Mat J, PetscBool use) { 171 PetscFunctionBegin; 172 PetscValidHeaderSpecific(J, MAT_CLASSID, 1); 173 PetscTryMethod(J, "MatSNESMFSetReuseBase_C", (Mat, PetscBool), (J, use)); 174 PetscFunctionReturn(0); 175 } 176 177 static PetscErrorCode MatSNESMFGetReuseBase_SNESMF(Mat J, PetscBool *use) { 178 PetscFunctionBegin; 179 if (J->ops->assemblyend == MatAssemblyEnd_SNESMF_UseBase) *use = PETSC_TRUE; 180 else *use = PETSC_FALSE; 181 PetscFunctionReturn(0); 182 } 183 184 /*@ 185 MatSNESMFGetReuseBase - Determines if the base vector is to be used for differencing even if the function provided to SNESSetFunction() is not the 186 same as that provided to MatMFFDSetFunction(). 187 188 Logically Collective on Mat 189 190 Input Parameter: 191 . J - the MatMFFD matrix 192 193 Output Parameter: 194 . use - if true always reuse the base vector instead of recomputing f(u) even if the function in the MatSNESMF is 195 not SNESComputeFunction() 196 197 Notes: 198 Care must be taken when using this routine to insure that the function provided to MatMFFDSetFunction(), call it F_MF() is compatible with 199 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 200 201 Developer Notes: 202 This was provided for the MOOSE team who desired to have a SNESSetFunction() function that could change configurations due 203 to contacts while the function provided to MatMFFDSetFunction() cannot. Except for the possibility of changing the configuration 204 both functions compute the same mathematical function so the differencing makes sense. 205 206 Level: advanced 207 208 .seealso: `MatCreateSNESMF()`, `MatSNESMFSetReuseBase()` 209 @*/ 210 PetscErrorCode MatSNESMFGetReuseBase(Mat J, PetscBool *use) { 211 PetscFunctionBegin; 212 PetscValidHeaderSpecific(J, MAT_CLASSID, 1); 213 PetscUseMethod(J, "MatSNESMFGetReuseBase_C", (Mat, PetscBool *), (J, use)); 214 PetscFunctionReturn(0); 215 } 216 217 /*@ 218 MatCreateSNESMF - Creates a matrix-free matrix context for use with 219 a SNES solver. This matrix can be used as the Jacobian argument for 220 the routine SNESSetJacobian(). See MatCreateMFFD() for details on how 221 the finite difference computation is done. 222 223 Collective on SNES 224 225 Input Parameters: 226 . snes - the SNES context 227 228 Output Parameter: 229 . J - the matrix-free matrix 230 231 Level: advanced 232 233 Notes: 234 You can call SNESSetJacobian() with MatMFFDComputeJacobian() if you are using matrix and not a different 235 preconditioner matrix 236 237 If you wish to provide a different function to do differencing on to compute the matrix-free operator than 238 that provided to SNESSetFunction() then call MatMFFDSetFunction() with your function after this call. 239 240 The difference between this routine and MatCreateMFFD() is that this matrix 241 automatically gets the current base vector from the SNES object and not from an 242 explicit call to MatMFFDSetBase(). 243 244 Warning: 245 If MatMFFDSetBase() is ever called on jac then this routine will NO longer get 246 the x from the SNES object and MatMFFDSetBase() must from that point on be used to 247 change the base vector x. 248 249 Warning: 250 Using a different function for the differencing will not work if you are using non-linear left preconditioning. 251 252 .seealso: `MatDestroy()`, `MatMFFDSetFunction()`, `MatMFFDSetFunctionError()`, `MatMFFDDSSetUmin()` 253 `MatMFFDSetHHistory()`, `MatMFFDResetHHistory()`, `MatCreateMFFD()`, 254 `MatMFFDGetH()`, `MatMFFDRegister()`, `MatMFFDComputeJacobian()`, `MatSNESMFSetReuseBase()`, `MatSNESMFGetReuseBase()` 255 256 @*/ 257 PetscErrorCode MatCreateSNESMF(SNES snes, Mat *J) { 258 PetscInt n, N; 259 MatMFFD mf; 260 261 PetscFunctionBegin; 262 if (snes->vec_func) { 263 PetscCall(VecGetLocalSize(snes->vec_func, &n)); 264 PetscCall(VecGetSize(snes->vec_func, &N)); 265 } else if (snes->dm) { 266 Vec tmp; 267 PetscCall(DMGetGlobalVector(snes->dm, &tmp)); 268 PetscCall(VecGetLocalSize(tmp, &n)); 269 PetscCall(VecGetSize(tmp, &N)); 270 PetscCall(DMRestoreGlobalVector(snes->dm, &tmp)); 271 } else SETERRQ(PetscObjectComm((PetscObject)snes), PETSC_ERR_ARG_WRONGSTATE, "Must call SNESSetFunction() or SNESSetDM() first"); 272 PetscCall(MatCreateMFFD(PetscObjectComm((PetscObject)snes), n, n, N, N, J)); 273 PetscCall(MatShellGetContext(*J, &mf)); 274 mf->ctx = snes; 275 276 if (snes->npc && snes->npcside == PC_LEFT) { 277 PetscCall(MatMFFDSetFunction(*J, (PetscErrorCode(*)(void *, Vec, Vec))SNESComputeFunctionDefaultNPC, snes)); 278 } else { 279 DM dm; 280 DMSNES dms; 281 282 PetscCall(SNESGetDM(snes, &dm)); 283 PetscCall(DMGetDMSNES(dm, &dms)); 284 PetscCall(MatMFFDSetFunction(*J, (PetscErrorCode(*)(void *, Vec, Vec))(dms->ops->computemffunction ? SNESComputeMFFunction : SNESComputeFunction), snes)); 285 } 286 (*J)->ops->assemblyend = MatAssemblyEnd_SNESMF; 287 288 PetscCall(PetscObjectComposeFunction((PetscObject)*J, "MatMFFDSetBase_C", MatMFFDSetBase_SNESMF)); 289 PetscCall(PetscObjectComposeFunction((PetscObject)*J, "MatSNESMFSetReuseBase_C", MatSNESMFSetReuseBase_SNESMF)); 290 PetscCall(PetscObjectComposeFunction((PetscObject)*J, "MatSNESMFGetReuseBase_C", MatSNESMFGetReuseBase_SNESMF)); 291 PetscFunctionReturn(0); 292 } 293