Line data Source code
1 : // The libMesh Finite Element Library.
2 : // Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 :
4 : // This library is free software; you can redistribute it and/or
5 : // modify it under the terms of the GNU Lesser General Public
6 : // License as published by the Free Software Foundation; either
7 : // version 2.1 of the License, or (at your option) any later version.
8 :
9 : // This library is distributed in the hope that it will be useful,
10 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : // Lesser General Public License for more details.
13 :
14 : // You should have received a copy of the GNU Lesser General Public
15 : // License along with this library; if not, write to the Free Software
16 : // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 :
18 :
19 :
20 : #include "libmesh/libmesh_common.h"
21 :
22 : #ifdef LIBMESH_HAVE_PETSC
23 :
24 : // Local Includes
25 : #include "libmesh/libmesh_logging.h"
26 : #include "libmesh/nonlinear_implicit_system.h"
27 : #include "libmesh/petsc_nonlinear_solver.h"
28 : #include "libmesh/petsc_linear_solver.h"
29 : #include "libmesh/petsc_vector.h"
30 : #include "libmesh/petsc_mffd_matrix.h"
31 : #include "libmesh/dof_map.h"
32 : #include "libmesh/preconditioner.h"
33 : #include "libmesh/solver_configuration.h"
34 : #include "libmesh/petscdmlibmesh.h"
35 : #include "libmesh/petsc_mffd_matrix.h"
36 :
37 : #if defined(LIBMESH_HAVE_PETSC_HYPRE) && PETSC_VERSION_LESS_THAN(3, 23, 0) && \
38 : !PETSC_VERSION_LESS_THAN(3, 12, 0) && defined(PETSC_HAVE_HYPRE_DEVICE)
39 : #include <HYPRE_utilities.h>
40 : #endif
41 :
42 : namespace libMesh
43 : {
44 : class ResidualContext
45 : {
46 : public:
47 5660 : ResidualContext(PetscNonlinearSolver<Number> * solver_in, NonlinearImplicitSystem & sys_in) :
48 : solver(solver_in),
49 5660 : sys(sys_in)
50 5660 : {}
51 :
52 : PetscNonlinearSolver<Number> * solver;
53 : NonlinearImplicitSystem & sys;
54 : };
55 :
56 : ResidualContext
57 281117 : libmesh_petsc_snes_residual_helper (SNES snes, Vec x, void * ctx)
58 : {
59 11320 : LOG_SCOPE("residual()", "PetscNonlinearSolver");
60 :
61 5660 : libmesh_assert(x);
62 5660 : libmesh_assert(ctx);
63 :
64 : // No way to safety-check this cast, since we got a void *...
65 5660 : PetscNonlinearSolver<Number> * solver =
66 : static_cast<PetscNonlinearSolver<Number> *> (ctx);
67 :
68 5660 : libmesh_parallel_only(solver->comm());
69 :
70 : // Get the current iteration number from the snes object,
71 : // store it in the PetscNonlinearSolver object for possible use
72 : // by the user's residual function.
73 : {
74 281117 : PetscInt n_iterations = 0;
75 281117 : LibmeshPetscCall2(solver->comm(), SNESGetIterationNumber(snes, &n_iterations));
76 281117 : solver->_current_nonlinear_iteration_number = cast_int<unsigned>(n_iterations);
77 : }
78 :
79 10912 : NonlinearImplicitSystem & sys = solver->system();
80 :
81 5660 : PetscVector<Number> & X_sys = *cast_ptr<PetscVector<Number> *>(sys.solution.get());
82 :
83 281117 : PetscVector<Number> X_global(x, sys.comm());
84 :
85 : // Use the system's update() to get a good local version of the
86 : // parallel solution. This operation does not modify the incoming
87 : // "x" vector, it only localizes information from "x" into
88 : // sys.current_local_solution.
89 281117 : X_global.swap(X_sys);
90 281117 : sys.update();
91 281117 : X_global.swap(X_sys);
92 :
93 : // Enforce constraints (if any) exactly on the
94 : // current_local_solution. This is the solution vector that is
95 : // actually used in the computation of the residual below, and is
96 : // not locked by debug-enabled PETSc the way that "x" is.
97 281117 : if (solver->_exact_constraint_enforcement)
98 281117 : sys.get_dof_map().enforce_constraints_exactly(sys, sys.current_local_solution.get());
99 :
100 292029 : return ResidualContext(solver, sys);
101 270205 : }
102 :
103 : //--------------------------------------------------------------------
104 : // Functions with C linkage to pass to PETSc. PETSc will call these
105 : // methods as needed.
106 : //
107 : // Since they must have C linkage they have no knowledge of a namespace.
108 : // Give them an obscure name to avoid namespace pollution.
109 : extern "C"
110 : {
111 : // -----------------------------------------------------------------
112 : // this function monitors the nonlinear solve and checks to see
113 : // if we want to recalculate the preconditioner. It only gets
114 : // added to the SNES instance if we're reusing the preconditioner
115 : PetscErrorCode
116 0 : libmesh_petsc_recalculate_monitor(SNES snes, PetscInt, PetscReal, void* ctx)
117 : {
118 : PetscFunctionBegin;
119 :
120 : // No way to safety-check this cast, since we got a void *...
121 0 : PetscNonlinearSolver<Number> * solver =
122 : static_cast<PetscNonlinearSolver<Number> *> (ctx);
123 :
124 : KSP ksp;
125 0 : LibmeshPetscCall2(solver->comm(), SNESGetKSP(snes, &ksp));
126 :
127 : PetscInt niter;
128 0 : LibmeshPetscCall2(solver->comm(), KSPGetIterationNumber(ksp, &niter));
129 :
130 0 : if (niter > cast_int<PetscInt>(solver->reuse_preconditioner_max_linear_its()))
131 : {
132 : // -2 is a magic number for "recalculate next time you need it
133 : // and then not again"
134 0 : LibmeshPetscCall2(solver->comm(), SNESSetLagPreconditioner(snes, -2));
135 : }
136 0 : PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
137 : }
138 :
139 : //-------------------------------------------------------------------
140 : // this function is called by PETSc at the end of each nonlinear step
141 : PetscErrorCode
142 105241 : libmesh_petsc_snes_monitor (SNES, PetscInt its, PetscReal fnorm, void *)
143 : {
144 : PetscFunctionBegin;
145 2896 : libMesh::out << " NL step "
146 2896 : << std::setw(2) << its
147 2896 : << std::scientific
148 2896 : << ", |residual|_2 = " << fnorm
149 2896 : << std::endl;
150 105241 : PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
151 : }
152 :
153 : #ifdef LIBMESH_ENABLE_DEPRECATED
154 : PetscErrorCode
155 0 : __libmesh_petsc_snes_monitor (SNES, PetscInt its, PetscReal fnorm, void *)
156 : {
157 : PetscFunctionBegin;
158 : libmesh_deprecated();
159 0 : PetscFunctionReturn(libmesh_petsc_snes_monitor(nullptr, its, fnorm, nullptr));
160 : }
161 : #endif
162 :
163 :
164 : //---------------------------------------------------------------
165 : // this function is called by PETSc to evaluate the residual at X
166 : PetscErrorCode
167 159137 : libmesh_petsc_snes_residual (SNES snes, Vec x, Vec r, void * ctx)
168 : {
169 : PetscFunctionBegin;
170 :
171 159137 : ResidualContext rc = libmesh_petsc_snes_residual_helper(snes, x, ctx);
172 :
173 3262 : libmesh_parallel_only(rc.sys.comm());
174 :
175 3262 : libmesh_assert(r);
176 165661 : PetscVector<Number> R(r, rc.sys.comm());
177 :
178 159137 : if (rc.solver->_zero_out_residual)
179 159137 : R.zero();
180 :
181 : //-----------------------------------------------------------------------------
182 : // if the user has provided both function pointers and objects only the pointer
183 : // will be used, so catch that as an error
184 159137 : libmesh_error_msg_if(rc.solver->residual && rc.solver->residual_object,
185 : "ERROR: cannot specify both a function and object to compute the Residual!");
186 :
187 159137 : libmesh_error_msg_if(rc.solver->matvec && rc.solver->residual_and_jacobian_object,
188 : "ERROR: cannot specify both a function and object to compute the combined Residual & Jacobian!");
189 :
190 159137 : if (rc.solver->residual != nullptr)
191 16 : rc.solver->residual(*rc.sys.current_local_solution.get(), R, rc.sys);
192 :
193 158857 : else if (rc.solver->residual_object != nullptr)
194 3277 : rc.solver->residual_object->residual(*rc.sys.current_local_solution.get(), R, rc.sys);
195 :
196 155678 : else if (rc.solver->matvec != nullptr)
197 0 : rc.solver->matvec (*rc.sys.current_local_solution.get(), &R, nullptr, rc.sys);
198 :
199 155678 : else if (rc.solver->residual_and_jacobian_object != nullptr)
200 : {
201 155678 : auto & jac = rc.sys.get_system_matrix();
202 :
203 155678 : if (rc.solver->_zero_out_jacobian)
204 155678 : jac.zero();
205 :
206 155678 : rc.solver->residual_and_jacobian_object->residual_and_jacobian(
207 6312 : *rc.sys.current_local_solution.get(), &R, &jac, rc.sys);
208 :
209 155678 : jac.close();
210 155678 : if (rc.solver->_exact_constraint_enforcement)
211 : {
212 155678 : rc.sys.get_dof_map().enforce_constraints_on_jacobian(rc.sys, &jac);
213 155678 : jac.close();
214 : }
215 : }
216 :
217 : else
218 0 : libmesh_error_msg("Error! Unable to compute residual and/or Jacobian!");
219 :
220 :
221 : // Synchronize PETSc x to local solution since the local solution may be changed due to the constraints
222 3262 : PetscVector<Number> & X_sys = *cast_ptr<PetscVector<Number> *>(rc.sys.solution.get());
223 162399 : PetscVector<Number> X_global(x, rc.sys.comm());
224 :
225 159137 : X_global.swap(X_sys);
226 159137 : rc.sys.update();
227 159137 : X_global.swap(X_sys);
228 :
229 159137 : R.close();
230 :
231 159137 : if (rc.solver->_exact_constraint_enforcement)
232 : {
233 159137 : rc.sys.get_dof_map().enforce_constraints_on_residual(rc.sys, &R, rc.sys.current_local_solution.get());
234 159137 : R.close();
235 : }
236 :
237 162399 : PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
238 152613 : }
239 :
240 : #ifdef LIBMESH_ENABLE_DEPRECATED
241 : PetscErrorCode
242 0 : __libmesh_petsc_snes_residual (SNES snes, Vec x, Vec r, void * ctx)
243 : {
244 : PetscFunctionBegin;
245 : libmesh_deprecated();
246 0 : PetscFunctionReturn(libmesh_petsc_snes_residual(snes, x, r, ctx));
247 : }
248 : #endif
249 :
250 : //-----------------------------------------------------------------------------------------
251 : // this function is called by PETSc to approximate the Jacobian at X via finite differences
252 : PetscErrorCode
253 0 : libmesh_petsc_snes_fd_residual (SNES snes, Vec x, Vec r, void * ctx)
254 : {
255 : PetscFunctionBegin;
256 :
257 0 : ResidualContext rc = libmesh_petsc_snes_residual_helper(snes, x, ctx);
258 :
259 0 : libmesh_parallel_only(rc.sys.comm());
260 :
261 0 : libmesh_assert(r);
262 0 : PetscVector<Number> R(r, rc.sys.comm());
263 :
264 0 : if (rc.solver->_zero_out_residual)
265 0 : R.zero();
266 :
267 0 : if (rc.solver->fd_residual_object != nullptr)
268 0 : rc.solver->fd_residual_object->residual(*rc.sys.current_local_solution.get(), R, rc.sys);
269 :
270 0 : else if (rc.solver->residual_object != nullptr)
271 0 : rc.solver->residual_object->residual(*rc.sys.current_local_solution.get(), R, rc.sys);
272 :
273 : else
274 0 : libmesh_error_msg("Error! Unable to compute residual for forming finite difference Jacobian!");
275 :
276 : // Synchronize PETSc x to local solution since the local solution may be changed due to the constraints
277 0 : PetscVector<Number> & X_sys = *cast_ptr<PetscVector<Number> *>(rc.sys.solution.get());
278 0 : PetscVector<Number> X_global(x, rc.sys.comm());
279 :
280 0 : X_global.swap(X_sys);
281 0 : rc.sys.update();
282 0 : X_global.swap(X_sys);
283 :
284 0 : R.close();
285 :
286 0 : if (rc.solver->_exact_constraint_enforcement)
287 : {
288 0 : rc.sys.get_dof_map().enforce_constraints_on_residual(rc.sys, &R, rc.sys.current_local_solution.get());
289 0 : R.close();
290 : }
291 :
292 0 : PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
293 0 : }
294 :
295 : #ifdef LIBMESH_ENABLE_DEPRECATED
296 : PetscErrorCode
297 0 : __libmesh_petsc_snes_fd_residual (SNES snes, Vec x, Vec r, void * ctx)
298 : {
299 : PetscFunctionBegin;
300 : libmesh_deprecated();
301 0 : PetscFunctionReturn(libmesh_petsc_snes_fd_residual(snes, x, r, ctx));
302 : }
303 : #endif
304 :
305 : //----------------------------------------------------------------
306 : // this function is called by PETSc to approximate Jacobian-vector
307 : // products at X via finite differences
308 : PetscErrorCode
309 121980 : libmesh_petsc_snes_mffd_residual (SNES snes, Vec x, Vec r, void * ctx)
310 : {
311 : PetscFunctionBegin;
312 :
313 121980 : ResidualContext rc = libmesh_petsc_snes_residual_helper(snes, x, ctx);
314 :
315 2398 : libmesh_parallel_only(rc.sys.comm());
316 :
317 2398 : libmesh_assert(r);
318 126368 : PetscVector<Number> R(r, rc.sys.comm());
319 :
320 121980 : if (rc.solver->_zero_out_residual)
321 121980 : R.zero();
322 :
323 121980 : if (rc.solver->mffd_residual_object != nullptr)
324 123970 : rc.solver->mffd_residual_object->residual(*rc.sys.current_local_solution.get(), R, rc.sys);
325 :
326 0 : else if (rc.solver->residual_object != nullptr)
327 0 : rc.solver->residual_object->residual(*rc.sys.current_local_solution.get(), R, rc.sys);
328 :
329 : else
330 0 : libmesh_error_msg("Error! Unable to compute residual for forming finite differenced"
331 : "Jacobian-vector products!");
332 :
333 : // Synchronize PETSc x to local solution since the local solution may be changed due to the constraints
334 2398 : PetscVector<Number> & X_sys = *cast_ptr<PetscVector<Number> *>(rc.sys.solution.get());
335 123970 : PetscVector<Number> X_global(x, rc.sys.comm());
336 :
337 121980 : X_global.swap(X_sys);
338 121980 : rc.sys.update();
339 121980 : X_global.swap(X_sys);
340 :
341 121980 : R.close();
342 :
343 121980 : if (rc.solver->_exact_constraint_enforcement)
344 : {
345 121980 : rc.sys.get_dof_map().enforce_constraints_on_residual(rc.sys, &R, rc.sys.current_local_solution.get());
346 121980 : R.close();
347 : }
348 :
349 124378 : PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
350 117592 : }
351 :
352 : //----------------------------------------------------------
353 : // this function serves an interface between the petsc layer
354 : // and the actual mffd residual computing routine
355 : PetscErrorCode
356 121980 : libmesh_petsc_snes_mffd_interface (void * ctx, Vec x, Vec r)
357 : {
358 : PetscFunctionBegin;
359 :
360 : // No way to safety-check this cast, since we got a void *...
361 2398 : PetscNonlinearSolver<Number> * solver =
362 : static_cast<PetscNonlinearSolver<Number> *> (ctx);
363 :
364 121980 : LibmeshPetscCall2(solver->comm(), libmesh_petsc_snes_mffd_residual(solver->snes(), x, r, ctx));
365 :
366 : #if !PETSC_VERSION_LESS_THAN(3,8,4)
367 : #ifndef NDEBUG
368 :
369 : // When the user requested to reuse the nonlinear residual as the base for doing matrix-free
370 : // approximation of the Jacobian, we'll do a sanity check to make sure that that was safe to do
371 2398 : if (solver->snes_mf_reuse_base() && (solver->comm().size() == 1) && (libMesh::n_threads() == 1))
372 : {
373 0 : SNES snes = solver->snes();
374 :
375 : KSP ksp;
376 0 : LibmeshPetscCall2(solver->comm(), SNESGetKSP(snes, &ksp));
377 :
378 : PetscInt ksp_it;
379 0 : LibmeshPetscCall2(solver->comm(), KSPGetIterationNumber(ksp, &ksp_it));
380 :
381 : SNESType snes_type;
382 0 : LibmeshPetscCall2(solver->comm(), SNESGetType(snes, &snes_type));
383 :
384 0 : libmesh_assert_msg(snes_type, "We're being called from SNES; snes_type should be non-null");
385 :
386 : Mat J;
387 0 : LibmeshPetscCall2(solver->comm(), SNESGetJacobian(snes, &J, NULL, NULL, NULL));
388 0 : libmesh_assert_msg(J, "We're being called from SNES; J should be non-null");
389 :
390 : MatType mat_type;
391 0 : LibmeshPetscCall2(solver->comm(), MatGetType(J, &mat_type));
392 0 : libmesh_assert_msg(mat_type, "We're being called from SNES; mat_type should be non-null");
393 :
394 0 : bool is_operator_mffd = strcmp(mat_type, MATMFFD) == 0;
395 :
396 0 : if ((ksp_it == PetscInt(0)) && is_operator_mffd)
397 : {
398 0 : bool computing_base_vector = solver->computing_base_vector();
399 :
400 0 : if (computing_base_vector)
401 : {
402 : Vec nonlinear_residual;
403 :
404 0 : LibmeshPetscCall2(solver->comm(), SNESGetFunction(snes, &nonlinear_residual, NULL, NULL));
405 :
406 : PetscBool vecs_equal;
407 0 : LibmeshPetscCall2(solver->comm(), VecEqual(r, nonlinear_residual, &vecs_equal));
408 :
409 0 : libmesh_error_msg_if(!(vecs_equal == PETSC_TRUE),
410 : "You requested to reuse the nonlinear residual vector as the base vector for "
411 : "computing the action of the matrix-free Jacobian, but the vectors are not "
412 : "the same. Your physics must have states; either remove the states "
413 : "from your code or make sure that you set_mf_reuse_base(false)");
414 : }
415 :
416 : // There are always exactly two function evaluations for the zeroth ksp iteration when doing
417 : // matrix-free approximation of the Jacobian action: one corresponding to the evaluation of
418 : // the base vector, and the other corresponding to evaluation of the perturbed vector. So we
419 : // toggle back and forth between states
420 0 : solver->set_computing_base_vector(!computing_base_vector);
421 : }
422 : }
423 : #endif
424 : #endif
425 :
426 121980 : PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
427 : }
428 :
429 : #ifdef LIBMESH_ENABLE_DEPRECATED
430 : PetscErrorCode
431 0 : __libmesh_petsc_snes_mffd_interface (void * ctx, Vec x, Vec r)
432 : {
433 : PetscFunctionBegin;
434 : libmesh_deprecated();
435 0 : PetscFunctionReturn(libmesh_petsc_snes_mffd_interface(ctx, x, r));
436 : }
437 : #endif
438 :
439 : //---------------------------------------------------------------
440 : // this function is called by PETSc to evaluate the Jacobian at X
441 : PetscErrorCode
442 76064 : libmesh_petsc_snes_jacobian(SNES snes, Vec x, Mat jac, Mat pc, void * ctx)
443 : {
444 : PetscFunctionBegin;
445 :
446 4120 : LOG_SCOPE("jacobian()", "PetscNonlinearSolver");
447 :
448 2060 : libmesh_assert(ctx);
449 :
450 : // No way to safety-check this cast, since we got a void *...
451 2060 : PetscNonlinearSolver<Number> * solver =
452 : static_cast<PetscNonlinearSolver<Number> *> (ctx);
453 :
454 2060 : libmesh_parallel_only(solver->comm());
455 :
456 : // Get the current iteration number from the snes object,
457 : // store it in the PetscNonlinearSolver object for possible use
458 : // by the user's Jacobian function.
459 : {
460 76064 : PetscInt n_iterations = 0;
461 76064 : LibmeshPetscCall2(solver->comm(), SNESGetIterationNumber(snes, &n_iterations));
462 76064 : solver->_current_nonlinear_iteration_number = cast_int<unsigned>(n_iterations);
463 : }
464 :
465 : //-----------------------------------------------------------------------------
466 : // if the user has provided both function pointers and objects only the pointer
467 : // will be used, so catch that as an error
468 76064 : libmesh_error_msg_if(solver->jacobian && solver->jacobian_object,
469 : "ERROR: cannot specify both a function and object to compute the Jacobian!");
470 :
471 76064 : libmesh_error_msg_if(solver->matvec && solver->residual_and_jacobian_object,
472 : "ERROR: cannot specify both a function and object to compute the combined Residual & Jacobian!");
473 :
474 4120 : NonlinearImplicitSystem & sys = solver->system();
475 :
476 76064 : PetscMatrixBase<Number> * const PC = pc ? PetscMatrixBase<Number>::get_context(pc, sys.comm()) : nullptr;
477 76064 : PetscMatrixBase<Number> * Jac = jac ? PetscMatrixBase<Number>::get_context(jac, sys.comm()) : nullptr;
478 2060 : PetscVector<Number> & X_sys = *cast_ptr<PetscVector<Number> *>(sys.solution.get());
479 80184 : PetscVector<Number> X_global(x, sys.comm());
480 :
481 6180 : PetscMFFDMatrix<Number> mffd_jac(sys.comm());
482 76064 : PetscBool p_is_shell = PETSC_FALSE;
483 76064 : PetscBool j_is_mffd = PETSC_FALSE;
484 76064 : PetscBool j_is_shell = PETSC_FALSE;
485 76064 : if (pc)
486 76064 : LibmeshPetscCall2(sys.comm(), PetscObjectTypeCompare((PetscObject)pc, MATSHELL, &p_is_shell));
487 2060 : libmesh_assert(jac);
488 76064 : LibmeshPetscCall2(sys.comm(), PetscObjectTypeCompare((PetscObject)jac, MATMFFD, &j_is_mffd));
489 76064 : LibmeshPetscCall2(sys.comm(), PetscObjectTypeCompare((PetscObject)jac, MATSHELL, &j_is_shell));
490 76064 : if (j_is_mffd == PETSC_TRUE)
491 : {
492 408 : libmesh_assert(!Jac);
493 408 : Jac = &mffd_jac;
494 408 : mffd_jac = jac;
495 : }
496 :
497 : // We already computed the Jacobian during the residual evaluation
498 76064 : if (solver->residual_and_jacobian_object)
499 : {
500 : // We could be doing matrix-free in which case we cannot rely on closing of explicit matrices
501 : // that occurs during the PETSc residual callback
502 73312 : if ((j_is_shell == PETSC_TRUE) || (j_is_mffd == PETSC_TRUE))
503 14154 : Jac->close();
504 :
505 73312 : if (pc && (p_is_shell == PETSC_TRUE))
506 0 : PC->close();
507 :
508 73312 : PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
509 : }
510 :
511 : // Set the dof maps
512 2836 : PC->attach_dof_map(sys.get_dof_map());
513 2836 : Jac->attach_dof_map(sys.get_dof_map());
514 :
515 : // Use the systems update() to get a good local version of the parallel solution
516 2752 : X_global.swap(X_sys);
517 2752 : sys.update();
518 2752 : X_global.swap(X_sys);
519 :
520 : // Enforce constraints (if any) exactly on the
521 : // current_local_solution. This is the solution vector that is
522 : // actually used in the computation of the residual below, and is
523 : // not locked by debug-enabled PETSc the way that "x" is.
524 2752 : if (solver->_exact_constraint_enforcement)
525 2752 : sys.get_dof_map().enforce_constraints_exactly(sys, sys.current_local_solution.get());
526 :
527 2752 : if (solver->_zero_out_jacobian)
528 2752 : PC->zero();
529 :
530 :
531 2752 : if (solver->jacobian != nullptr)
532 210 : solver->jacobian(*sys.current_local_solution.get(), *PC, sys);
533 :
534 2542 : else if (solver->jacobian_object != nullptr)
535 2620 : solver->jacobian_object->jacobian(*sys.current_local_solution.get(), *PC, sys);
536 :
537 0 : else if (solver->matvec != nullptr)
538 0 : solver->matvec(*sys.current_local_solution.get(), nullptr, PC, sys);
539 :
540 : else
541 0 : libmesh_error_msg("Error! Unable to compute residual and/or Jacobian!");
542 :
543 2752 : PC->close();
544 2752 : if (solver->_exact_constraint_enforcement)
545 : {
546 2752 : sys.get_dof_map().enforce_constraints_on_jacobian(sys, PC);
547 2752 : PC->close();
548 : }
549 :
550 2752 : if (Jac != PC)
551 : {
552 : // Assume that shells know what they're doing
553 0 : libmesh_assert(!solver->_exact_constraint_enforcement || (j_is_mffd == PETSC_TRUE) ||
554 : (j_is_shell == PETSC_TRUE));
555 0 : Jac->close();
556 : }
557 :
558 84 : PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
559 71944 : }
560 :
561 : // This function gets called by PETSc in place of the standard Petsc line searches
562 : // if a linesearch object is supplied to the PetscNonlinearSolver class. It wraps
563 : // the linesearch algorithm implemented on the linesearch object.
564 : // * "linesearch" is an object that can be used to access the non-linear and linear solution
565 : // vectors as well as the residual and SNES object
566 : // * "ctx" is the PetscNonlinearSolver context
567 0 : PetscErrorCode libmesh_petsc_linesearch_shellfunc (SNESLineSearch linesearch, void * ctx)
568 : {
569 : PetscFunctionBegin;
570 :
571 : // No way to safety-check this cast, since we got a void *...
572 0 : PetscNonlinearSolver<Number> * solver =
573 : static_cast<PetscNonlinearSolver<Number> *> (ctx);
574 :
575 0 : libmesh_parallel_only(solver->comm());
576 :
577 0 : solver->linesearch_object->linesearch(linesearch);
578 0 : PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
579 : }
580 :
581 : #ifdef LIBMESH_ENABLE_DEPRECATED
582 : PetscErrorCode
583 0 : __libmesh_petsc_snes_jacobian(SNES snes, Vec x, Mat jac, Mat pc, void * ctx)
584 : {
585 : PetscFunctionBegin;
586 : libmesh_deprecated();
587 0 : PetscFunctionReturn(libmesh_petsc_snes_jacobian(snes, x, jac, pc, ctx));
588 : }
589 : #endif
590 :
591 : // This function gets called by PETSc after the SNES linesearch is
592 : // complete. We use it to exactly enforce any constraints on the
593 : // solution which may have drifted during the linear solve. In the
594 : // PETSc nomenclature:
595 : // * "x" is the old solution vector,
596 : // * "y" is the search direction (Newton step) vector,
597 : // * "w" is the candidate solution vector, and
598 : // the user is responsible for setting changed_y and changed_w
599 : // appropriately, depending on whether or not the search
600 : // direction or solution vector was changed, respectively.
601 210 : PetscErrorCode libmesh_petsc_snes_postcheck(SNESLineSearch, Vec x, Vec y, Vec w, PetscBool * changed_y, PetscBool * changed_w, void * context)
602 : {
603 : PetscFunctionBegin;
604 :
605 12 : LOG_SCOPE("postcheck()", "PetscNonlinearSolver");
606 :
607 : // PETSc almost certainly initializes these to false already, but
608 : // it doesn't hurt to be explicit.
609 210 : *changed_w = PETSC_FALSE;
610 210 : *changed_y = PETSC_FALSE;
611 :
612 6 : libmesh_assert(context);
613 :
614 : // Cast the context to a NonlinearSolver object.
615 6 : PetscNonlinearSolver<Number> * solver =
616 : static_cast<PetscNonlinearSolver<Number> *> (context);
617 :
618 6 : libmesh_parallel_only(solver->comm());
619 :
620 : // If the user has provided both postcheck function pointer and
621 : // object, this is ambiguous, so throw an error.
622 210 : libmesh_error_msg_if(solver->postcheck && solver->postcheck_object,
623 : "ERROR: cannot specify both a function and object for performing the solve postcheck!");
624 :
625 : // It's also possible that we don't need to do anything at all, in
626 : // that case return early...
627 12 : NonlinearImplicitSystem & sys = solver->system();
628 :
629 210 : if (!solver->postcheck && !solver->postcheck_object)
630 0 : PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
631 :
632 : // We definitely need to wrap at least "w"
633 216 : PetscVector<Number> petsc_w(w, sys.comm());
634 :
635 : // The user sets these flags in his/her postcheck function to
636 : // indicate whether they changed something.
637 : bool
638 210 : changed_search_direction = false,
639 210 : changed_new_soln = false;
640 :
641 210 : if (solver->postcheck || solver->postcheck_object)
642 : {
643 222 : PetscVector<Number> petsc_x(x, sys.comm());
644 222 : PetscVector<Number> petsc_y(y, sys.comm());
645 :
646 210 : if (solver->postcheck)
647 0 : solver->postcheck(petsc_x,
648 : petsc_y,
649 : petsc_w,
650 : changed_search_direction,
651 : changed_new_soln,
652 : sys);
653 :
654 210 : else if (solver->postcheck_object)
655 216 : solver->postcheck_object->postcheck(petsc_x,
656 : petsc_y,
657 : petsc_w,
658 : changed_search_direction,
659 : changed_new_soln,
660 12 : sys);
661 198 : }
662 :
663 : // Record whether the user changed the solution or the search direction.
664 210 : if (changed_search_direction)
665 0 : *changed_y = PETSC_TRUE;
666 :
667 210 : if (changed_new_soln)
668 0 : *changed_w = PETSC_TRUE;
669 :
670 6 : PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
671 198 : }
672 :
673 : #ifdef LIBMESH_ENABLE_DEPRECATED
674 0 : PetscErrorCode __libmesh_petsc_snes_postcheck(SNESLineSearch, Vec x, Vec y, Vec w, PetscBool * changed_y, PetscBool * changed_w, void * context)
675 : {
676 : PetscFunctionBegin;
677 : libmesh_deprecated();
678 0 : PetscFunctionReturn(libmesh_petsc_snes_postcheck(nullptr, x, y, w, changed_y, changed_w, context));
679 : }
680 : #endif
681 :
682 0 : PetscErrorCode libmesh_petsc_snes_precheck(SNESLineSearch, Vec X, Vec Y, PetscBool * changed, void * context)
683 : {
684 : PetscFunctionBegin;
685 :
686 0 : LOG_SCOPE("precheck()", "PetscNonlinearSolver");
687 :
688 : // PETSc almost certainly initializes these to false already, but
689 : // it doesn't hurt to be explicit.
690 0 : *changed = PETSC_FALSE;
691 :
692 0 : libmesh_assert(context);
693 :
694 : // Cast the context to a NonlinearSolver object.
695 0 : PetscNonlinearSolver<Number> * solver =
696 : static_cast<PetscNonlinearSolver<Number> *> (context);
697 :
698 0 : libmesh_parallel_only(solver->comm());
699 :
700 : // It's possible that we don't need to do anything at all, in
701 : // that case return early...
702 0 : if (!solver->precheck_object)
703 0 : PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
704 :
705 : // The user sets these flags in his/her postcheck function to
706 : // indicate whether they changed something.
707 : bool
708 0 : petsc_changed = false;
709 :
710 0 : auto & sys = solver->system();
711 0 : auto & x_sys = *cast_ptr<PetscVector<Number> *>(sys.solution.get());
712 0 : PetscVector<Number> petsc_x(X, sys.comm());
713 0 : PetscVector<Number> petsc_y(Y, sys.comm());
714 :
715 : // Use the systems update() to get a good local version of the parallel solution
716 0 : petsc_x.swap(x_sys);
717 0 : sys.update();
718 0 : petsc_x.swap(x_sys);
719 :
720 : // Enforce constraints (if any) exactly on the
721 : // current_local_solution. This is the solution vector that is
722 : // actually used in the computation of residuals and Jacobians, and is
723 : // not locked by debug-enabled PETSc the way that "x" is.
724 0 : libmesh_assert(sys.current_local_solution.get());
725 0 : auto & local_soln = *sys.current_local_solution.get();
726 0 : if (solver->_exact_constraint_enforcement)
727 0 : sys.get_dof_map().enforce_constraints_exactly(sys, &local_soln);
728 :
729 0 : solver->precheck_object->precheck(local_soln,
730 : petsc_y,
731 : petsc_changed,
732 0 : sys);
733 :
734 : // Record whether the user changed the solution or the search direction.
735 0 : if (petsc_changed)
736 0 : *changed = PETSC_TRUE;
737 :
738 0 : PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
739 0 : }
740 :
741 : } // end extern "C"
742 :
743 :
744 :
745 : //---------------------------------------------------------------------
746 : // PetscNonlinearSolver<> methods
747 : template <typename T>
748 1470 : PetscNonlinearSolver<T>::PetscNonlinearSolver (sys_type & system_in) :
749 : NonlinearSolver<T>(system_in),
750 1386 : linesearch_object(nullptr),
751 1386 : _reason(SNES_CONVERGED_ITERATING/*==0*/), // Arbitrary initial value...
752 1386 : _n_linear_iterations(0),
753 1386 : _current_nonlinear_iteration_number(0),
754 1386 : _zero_out_residual(true),
755 1386 : _zero_out_jacobian(true),
756 1386 : _default_monitor(true),
757 1386 : _snesmf_reuse_base(true),
758 1386 : _computing_base_vector(true),
759 1386 : _setup_reuse(false),
760 1470 : _mffd_jac(this->_communicator)
761 : {
762 1470 : }
763 :
764 :
765 :
766 : template <typename T>
767 2772 : PetscNonlinearSolver<T>::~PetscNonlinearSolver () = default;
768 :
769 :
770 :
771 : template <typename T>
772 29680 : void PetscNonlinearSolver<T>::clear ()
773 : {
774 29680 : if (this->initialized())
775 : {
776 29400 : this->_is_initialized = false;
777 :
778 : // If we don't need the preconditioner next time
779 : // retain the original behavior of clearing the data
780 : // between solves.
781 29400 : if (!(reuse_preconditioner()))
782 : {
783 : // SNESReset really ought to work but replacing destroy() with
784 : // SNESReset causes a very slight change in behavior that
785 : // manifests as two failed MOOSE tests...
786 29400 : _snes.destroy();
787 : }
788 :
789 : // Reset the nonlinear iteration counter. This information is only relevant
790 : // *during* the solve(). After the solve is completed it should return to
791 : // the default value of 0.
792 29400 : _current_nonlinear_iteration_number = 0;
793 : }
794 29680 : }
795 :
796 : template <typename T>
797 180780 : void PetscNonlinearSolver<T>::init (const char * name)
798 : {
799 4078 : parallel_object_only();
800 :
801 : // Initialize the data structures if not done so already.
802 180780 : if (!this->initialized())
803 : {
804 29400 : this->_is_initialized = true;
805 :
806 : // Make only if we don't already have a retained snes
807 : // hanging around from the last solve
808 29400 : if (!_snes)
809 29400 : LibmeshPetscCall(SNESCreate(this->comm().get(), _snes.get()));
810 :
811 : // I believe all of the following can be safely repeated
812 : // even on an old snes instance from the last solve
813 :
814 29400 : if (name)
815 : {
816 0 : libmesh_assert(std::string(name).front() != '-');
817 0 : libmesh_assert(std::string(name).back() == '_');
818 0 : LibmeshPetscCall(SNESSetOptionsPrefix(_snes, name));
819 : }
820 :
821 : // Attaching a DM to SNES.
822 : #if defined(LIBMESH_ENABLE_AMR) && defined(LIBMESH_HAVE_METAPHYSICL)
823 57960 : bool use_petsc_dm = libMesh::on_command_line(
824 27720 : "--" + (name ? std::string(name) : std::string("")) + "use_petsc_dm");
825 :
826 : // This needs to be called before SNESSetFromOptions
827 29400 : if (use_petsc_dm)
828 0 : this->_dm_wrapper.init_and_attach_petscdm(this->system(), _snes);
829 : else
830 : #endif
831 : {
832 1680 : WrappedPetsc<DM> dm;
833 29400 : LibmeshPetscCall(DMCreate(this->comm().get(), dm.get()));
834 29400 : LibmeshPetscCall(DMSetType(dm, DMLIBMESH));
835 29400 : LibmeshPetscCall(DMlibMeshSetSystem(dm, this->system()));
836 :
837 29400 : if (name)
838 0 : LibmeshPetscCall(DMSetOptionsPrefix(dm, name));
839 :
840 29400 : LibmeshPetscCall(DMSetFromOptions(dm));
841 29400 : LibmeshPetscCall(DMSetUp(dm));
842 29400 : LibmeshPetscCall(SNESSetDM(_snes, dm));
843 : // SNES now owns the reference to dm.
844 : }
845 :
846 29400 : setup_default_monitor();
847 :
848 : // If the SolverConfiguration object is provided, use it to set
849 : // options during solver initialization.
850 29400 : if (this->_solver_configuration)
851 : {
852 0 : this->_solver_configuration->set_options_during_init();
853 : }
854 :
855 29400 : if (this->_preconditioner)
856 : {
857 : KSP ksp;
858 280 : LibmeshPetscCall(SNESGetKSP (_snes, &ksp));
859 : PC pc;
860 280 : LibmeshPetscCall(KSPGetPC(ksp,&pc));
861 :
862 280 : this->_preconditioner->init();
863 :
864 280 : LibmeshPetscCall(PCSetType(pc, PCSHELL));
865 280 : LibmeshPetscCall(PCShellSetContext(pc,(void *)this->_preconditioner));
866 :
867 : //Re-Use the shell functions from petsc_linear_solver
868 280 : LibmeshPetscCall(PCShellSetSetUp(pc,libmesh_petsc_preconditioner_setup));
869 280 : LibmeshPetscCall(PCShellSetApply(pc,libmesh_petsc_preconditioner_apply));
870 : }
871 : }
872 :
873 :
874 : // Tell PETSc about our linesearch "post-check" function, but only
875 : // if the user has provided one. There seem to be extra,
876 : // unnecessary residual calculations if a postcheck function is
877 : // attached for no reason.
878 180780 : if (this->postcheck || this->postcheck_object)
879 : {
880 : SNESLineSearch linesearch;
881 140 : LibmeshPetscCall(SNESGetLineSearch(_snes, &linesearch));
882 :
883 140 : LibmeshPetscCall(SNESLineSearchSetPostCheck(linesearch, libmesh_petsc_snes_postcheck, this));
884 : }
885 :
886 180780 : if (this->precheck_object)
887 : {
888 : SNESLineSearch linesearch;
889 0 : LibmeshPetscCall(SNESGetLineSearch(_snes, &linesearch));
890 :
891 0 : LibmeshPetscCall(SNESLineSearchSetPreCheck(linesearch, libmesh_petsc_snes_precheck, this));
892 : }
893 180780 : }
894 :
895 :
896 : template <typename T>
897 4388 : SNES PetscNonlinearSolver<T>::snes(const char * name)
898 : {
899 121980 : this->init(name);
900 4388 : return _snes;
901 : }
902 :
903 :
904 :
905 : template <typename T>
906 : void
907 0 : PetscNonlinearSolver<T>::build_mat_null_space(NonlinearImplicitSystem::ComputeVectorSubspace * computeSubspaceObject,
908 : void (*computeSubspace)(std::vector<NumericVector<Number> *> &, sys_type &),
909 : MatNullSpace * msp)
910 : {
911 0 : parallel_object_only();
912 :
913 0 : std::vector<NumericVector<Number> *> sp;
914 0 : if (computeSubspaceObject)
915 0 : (*computeSubspaceObject)(sp, this->system());
916 : else
917 0 : (*computeSubspace)(sp, this->system());
918 :
919 0 : *msp = LIBMESH_PETSC_NULLPTR;
920 0 : if (sp.size())
921 : {
922 0 : PetscInt nmodes = cast_int<PetscInt>(sp.size());
923 :
924 0 : std::vector<Vec> modes(nmodes);
925 0 : std::vector<PetscScalar> dots(nmodes);
926 :
927 0 : for (PetscInt i=0; i<nmodes; ++i)
928 : {
929 0 : auto pv = cast_ptr<PetscVector<T> *>(sp[i]);
930 :
931 0 : LibmeshPetscCall(VecDuplicate(pv->vec(), &modes[i]));
932 :
933 0 : LibmeshPetscCall(VecCopy(pv->vec(), modes[i]));
934 : }
935 :
936 : // Normalize.
937 0 : LibmeshPetscCall(VecNormalize(modes[0], LIBMESH_PETSC_NULLPTR));
938 :
939 0 : for (PetscInt i=1; i<nmodes; i++)
940 : {
941 : // Orthonormalize vec[i] against vec[0:i-1]
942 0 : LibmeshPetscCall(VecMDot(modes[i], i, modes.data(), dots.data()));
943 :
944 0 : for (PetscInt j=0; j<i; j++)
945 0 : dots[j] *= -1.;
946 :
947 0 : LibmeshPetscCall(VecMAXPY(modes[i], i, dots.data(), modes.data()));
948 :
949 0 : LibmeshPetscCall(VecNormalize(modes[i], LIBMESH_PETSC_NULLPTR));
950 : }
951 :
952 0 : LibmeshPetscCall(MatNullSpaceCreate(this->comm().get(), PETSC_FALSE, nmodes, modes.data(), msp));
953 :
954 0 : for (PetscInt i=0; i<nmodes; ++i)
955 0 : LibmeshPetscCall(VecDestroy(&modes[i]));
956 : }
957 0 : }
958 :
959 : template <typename T>
960 : std::pair<unsigned int, Real>
961 29400 : PetscNonlinearSolver<T>::solve (SparseMatrix<T> & pre_in, // System Preconditioning Matrix
962 : NumericVector<T> & x_in, // Solution vector
963 : NumericVector<T> & r_in, // Residual vector
964 : const double, // Stopping tolerance
965 : const unsigned int)
966 : {
967 840 : parallel_object_only();
968 :
969 840 : LOG_SCOPE("solve()", "PetscNonlinearSolver");
970 29400 : this->init ();
971 :
972 : // Make sure the data passed in are really of Petsc types
973 840 : PetscMatrixBase<T> * pre = cast_ptr<PetscMatrixBase<T> *>(&pre_in);
974 840 : PetscVector<T> * x = cast_ptr<PetscVector<T> *>(&x_in);
975 840 : PetscVector<T> * r = cast_ptr<PetscVector<T> *>(&r_in);
976 :
977 29400 : PetscInt n_iterations =0;
978 : // Should actually be a PetscReal, but I don't know which version of PETSc first introduced PetscReal
979 29400 : Real final_residual_norm=0.;
980 :
981 : // We don't want to do this twice because it resets
982 : // SNESSetLagPreconditioner
983 29400 : if ((reuse_preconditioner()) && (!_setup_reuse))
984 : {
985 0 : _setup_reuse = true;
986 0 : LibmeshPetscCall(SNESSetLagPreconditionerPersists(_snes, PETSC_TRUE));
987 : // According to the PETSC 3.16.5 docs -2 is a magic number which
988 : // means "recalculate the next time you need it and then not again"
989 0 : LibmeshPetscCall(SNESSetLagPreconditioner(_snes, -2));
990 : // Add in our callback which will trigger recalculating
991 : // the preconditioner when we hit reuse_preconditioner_max_linear_its
992 0 : LibmeshPetscCall(SNESMonitorSet(_snes, &libmesh_petsc_recalculate_monitor,
993 : this,
994 : NULL));
995 : }
996 29400 : else if (!(reuse_preconditioner()))
997 : // This covers the case where it was enabled but was then disabled
998 : {
999 29400 : LibmeshPetscCall(SNESSetLagPreconditionerPersists(_snes, PETSC_FALSE));
1000 29400 : if (_setup_reuse)
1001 : {
1002 0 : _setup_reuse = false;
1003 0 : LibmeshPetscCall(SNESMonitorCancel(_snes));
1004 : // Readd default monitor
1005 0 : setup_default_monitor();
1006 : }
1007 : }
1008 :
1009 29400 : LibmeshPetscCall(SNESSetFunction (_snes, r->vec(), libmesh_petsc_snes_residual, this));
1010 :
1011 : // Only set the jacobian function if we've been provided with something to call.
1012 : // This allows a user to set their own jacobian function if they want to
1013 29400 : if (this->jacobian || this->jacobian_object || this->residual_and_jacobian_object)
1014 29400 : LibmeshPetscCall(SNESSetJacobian (_snes, pre->mat(), pre->mat(), libmesh_petsc_snes_jacobian, this));
1015 :
1016 : // Have the Krylov subspace method use our good initial guess rather than 0
1017 : KSP ksp;
1018 29400 : LibmeshPetscCall(SNESGetKSP (_snes, &ksp));
1019 :
1020 : // Set the tolerances for the iterative solver. Use the user-supplied
1021 : // tolerance for the relative residual & leave the others at default values
1022 29400 : LibmeshPetscCall(KSPSetTolerances (ksp, this->initial_linear_tolerance, PETSC_DEFAULT,
1023 : PETSC_DEFAULT, this->max_linear_iterations));
1024 :
1025 : // Set the tolerances for the non-linear solver.
1026 29400 : LibmeshPetscCall(SNESSetTolerances(_snes,
1027 : this->absolute_residual_tolerance,
1028 : this->relative_residual_tolerance,
1029 : this->relative_step_tolerance,
1030 : this->max_nonlinear_iterations,
1031 : this->max_function_evaluations));
1032 :
1033 : // Set the divergence tolerance for the non-linear solver
1034 : #if !PETSC_VERSION_LESS_THAN(3,8,0)
1035 29400 : LibmeshPetscCall(SNESSetDivergenceTolerance(_snes, this->divergence_tolerance));
1036 : #endif
1037 :
1038 : //Pull in command-line options
1039 : #if PETSC_VERSION_LESS_THAN(3,7,0)
1040 : LibmeshPetscCall(KSPSetFromOptions(ksp));
1041 : #endif
1042 29400 : LibmeshPetscCall(SNESSetFromOptions(_snes));
1043 :
1044 : #if defined(LIBMESH_HAVE_PETSC_HYPRE) && PETSC_VERSION_LESS_THAN(3, 23, 0) && \
1045 : !PETSC_VERSION_LESS_THAN(3, 12, 0) && defined(PETSC_HAVE_HYPRE_DEVICE)
1046 : {
1047 : // Make sure hypre has been initialized
1048 : LibmeshPetscCallExternal(HYPRE_Initialize);
1049 : PetscScalar * dummyarray;
1050 : PetscMemType mtype;
1051 : LibmeshPetscCall(VecGetArrayAndMemType(x->vec(), &dummyarray, &mtype));
1052 : LibmeshPetscCall(VecRestoreArrayAndMemType(x->vec(), &dummyarray));
1053 : if (PetscMemTypeHost(mtype))
1054 : LibmeshPetscCallExternal(HYPRE_SetMemoryLocation, HYPRE_MEMORY_HOST);
1055 : }
1056 : #endif
1057 :
1058 29400 : if (this->user_presolve)
1059 0 : this->user_presolve(this->system());
1060 :
1061 : //Set the preconditioning matrix
1062 29400 : if (this->_preconditioner)
1063 : {
1064 8 : this->_preconditioner->set_matrix(pre_in);
1065 280 : this->_preconditioner->init();
1066 : }
1067 :
1068 : // If the SolverConfiguration object is provided, use it to override
1069 : // solver options.
1070 29400 : if (this->_solver_configuration)
1071 0 : this->_solver_configuration->configure_solver();
1072 :
1073 : // In PETSc versions before 3.5.0, it is not possible to call
1074 : // SNESSetUp() before the solution and rhs vectors are initialized, as
1075 : // this triggers the
1076 : //
1077 : // "Solution vector cannot be right hand side vector!"
1078 : //
1079 : // error message. It is also not possible to call SNESSetSolution()
1080 : // in those versions of PETSc to work around the problem, since that
1081 : // API was removed in 3.0.0 and only restored in 3.6.0. The
1082 : // overzealous check was moved out of SNESSetUp in PETSc 3.5.0
1083 : // (petsc/petsc@154060b), so this code block should be safe to use
1084 : // in 3.5.0 and later.
1085 : #if !PETSC_VERSION_LESS_THAN(3,6,0)
1086 29400 : LibmeshPetscCall(SNESSetSolution(_snes, x->vec()));
1087 : #endif
1088 29400 : LibmeshPetscCall(SNESSetUp(_snes));
1089 :
1090 : Mat J, P;
1091 29400 : LibmeshPetscCall(SNESGetJacobian(_snes, &J, &P,
1092 : LIBMESH_PETSC_NULLPTR,
1093 : LIBMESH_PETSC_NULLPTR));
1094 29400 : LibmeshPetscCall(MatMFFDSetFunction(J, libmesh_petsc_snes_mffd_interface, this));
1095 : #if !PETSC_VERSION_LESS_THAN(3,8,4)
1096 : #ifndef NDEBUG
1097 : // If we're in debug mode, do not reuse the nonlinear function evaluation as the base for doing
1098 : // matrix-free approximations of the Jacobian action. Instead if the user requested that we reuse
1099 : // the base, we'll check the base function evaluation and compare it to the nonlinear residual
1100 : // evaluation. If they are different, then we'll error and inform the user that it's unsafe to
1101 : // reuse the base
1102 840 : LibmeshPetscCall(MatSNESMFSetReuseBase(J, PETSC_FALSE));
1103 : #else
1104 : // Resue the residual vector from SNES
1105 28560 : LibmeshPetscCall(MatSNESMFSetReuseBase(J, static_cast<PetscBool>(_snesmf_reuse_base)));
1106 : #endif
1107 : #endif
1108 :
1109 : // Only set the nullspace if we have a way of computing it and the result is non-empty.
1110 29400 : if (this->nullspace || this->nullspace_object)
1111 : {
1112 0 : WrappedPetsc<MatNullSpace> msp;
1113 0 : this->build_mat_null_space(this->nullspace_object, this->nullspace, msp.get());
1114 0 : if (msp)
1115 : {
1116 0 : LibmeshPetscCall(MatSetNullSpace(J, msp));
1117 0 : if (P != J)
1118 0 : LibmeshPetscCall(MatSetNullSpace(P, msp));
1119 : }
1120 : }
1121 :
1122 : // Only set the transpose nullspace if we have a way of computing it and the result is non-empty.
1123 29400 : if (this->transpose_nullspace || this->transpose_nullspace_object)
1124 : {
1125 : #if PETSC_VERSION_LESS_THAN(3,6,0)
1126 : libmesh_warning("MatSetTransposeNullSpace is only supported for PETSc >= 3.6, transpose nullspace will be ignored.");
1127 : #else
1128 0 : WrappedPetsc<MatNullSpace> msp;
1129 0 : this->build_mat_null_space(this->transpose_nullspace_object, this->transpose_nullspace, msp.get());
1130 0 : if (msp)
1131 : {
1132 0 : LibmeshPetscCall(MatSetTransposeNullSpace(J, msp));
1133 0 : if (P != J)
1134 0 : LibmeshPetscCall(MatSetTransposeNullSpace(P, msp));
1135 : }
1136 : #endif
1137 : }
1138 :
1139 : // Only set the nearnullspace if we have a way of computing it and the result is non-empty.
1140 29400 : if (this->nearnullspace || this->nearnullspace_object)
1141 : {
1142 0 : WrappedPetsc<MatNullSpace> msp;
1143 0 : this->build_mat_null_space(this->nearnullspace_object, this->nearnullspace, msp.get());
1144 :
1145 0 : if (msp)
1146 : {
1147 0 : LibmeshPetscCall(MatSetNearNullSpace(J, msp));
1148 0 : if (P != J)
1149 0 : LibmeshPetscCall(MatSetNearNullSpace(P, msp));
1150 : }
1151 : }
1152 :
1153 : SNESLineSearch linesearch;
1154 29400 : if (linesearch_object)
1155 : {
1156 0 : LibmeshPetscCall(SNESGetLineSearch(_snes, &linesearch));
1157 0 : LibmeshPetscCall(SNESLineSearchSetType(linesearch, SNESLINESEARCHSHELL));
1158 : #if PETSC_RELEASE_GREATER_EQUALS(3, 21, 0)
1159 : LibmeshPetscCall(SNESLineSearchShellSetApply(linesearch, libmesh_petsc_linesearch_shellfunc, this));
1160 : #else
1161 0 : LibmeshPetscCall(SNESLineSearchShellSetUserFunc(linesearch, libmesh_petsc_linesearch_shellfunc, this));
1162 : #endif
1163 : }
1164 :
1165 29400 : LibmeshPetscCall(SNESSolve (_snes, LIBMESH_PETSC_NULLPTR, x->vec()));
1166 :
1167 29400 : LibmeshPetscCall(SNESGetIterationNumber(_snes, &n_iterations));
1168 :
1169 29400 : LibmeshPetscCall(SNESGetLinearSolveIterations(_snes, &_n_linear_iterations));
1170 :
1171 : // SNESGetFunction has been around forever and should work on all
1172 : // versions of PETSc. This is also now the recommended approach
1173 : // according to the documentation for the PETSc 3.5.1 release:
1174 : // http://www.mcs.anl.gov/petsc/documentation/changes/35.html
1175 : Vec f;
1176 29400 : LibmeshPetscCall(SNESGetFunction(_snes, &f, 0, 0));
1177 29400 : LibmeshPetscCall(VecNorm(f, NORM_2, pPR(&final_residual_norm)));
1178 :
1179 : // Get and store the reason for convergence
1180 29400 : LibmeshPetscCall(SNESGetConvergedReason(_snes, &_reason));
1181 :
1182 : //Based on Petsc 2.3.3 documentation all diverged reasons are negative
1183 29400 : this->converged = (_reason >= 0);
1184 :
1185 : // Reset data structure
1186 29400 : this->clear();
1187 :
1188 : // return the # of its. and the final residual norm.
1189 31080 : return std::make_pair(n_iterations, final_residual_norm);
1190 : }
1191 :
1192 :
1193 :
1194 : template <typename T>
1195 280 : void PetscNonlinearSolver<T>::print_converged_reason()
1196 : {
1197 :
1198 8 : libMesh::out << "Nonlinear solver convergence/divergence reason: "
1199 280 : << SNESConvergedReasons[this->get_converged_reason()] << std::endl;
1200 280 : }
1201 :
1202 :
1203 :
1204 : template <typename T>
1205 280 : SNESConvergedReason PetscNonlinearSolver<T>::get_converged_reason()
1206 : {
1207 280 : if (this->initialized())
1208 0 : LibmeshPetscCall(SNESGetConvergedReason(_snes, &_reason));
1209 :
1210 280 : return _reason;
1211 : }
1212 :
1213 : template <typename T>
1214 0 : int PetscNonlinearSolver<T>::get_total_linear_iterations()
1215 : {
1216 0 : return _n_linear_iterations;
1217 : }
1218 :
1219 : template <typename T>
1220 29400 : void PetscNonlinearSolver<T>::setup_default_monitor()
1221 : {
1222 29400 : if (_default_monitor)
1223 29400 : LibmeshPetscCall(
1224 : SNESMonitorSet(_snes, libmesh_petsc_snes_monitor, this, LIBMESH_PETSC_NULLPTR));
1225 29400 : }
1226 :
1227 : template <typename T>
1228 88200 : bool PetscNonlinearSolver<T>::reuse_preconditioner() const
1229 : {
1230 88200 : return this->_reuse_preconditioner;
1231 : }
1232 :
1233 : template <typename T>
1234 0 : unsigned int PetscNonlinearSolver<T>::reuse_preconditioner_max_linear_its() const
1235 : {
1236 0 : return this->_reuse_preconditioner_max_linear_its;
1237 : }
1238 :
1239 : template <typename T>
1240 0 : void PetscNonlinearSolver<T>::force_new_preconditioner()
1241 : {
1242 : // Easiest way is just to clear everything out
1243 0 : this->_is_initialized = false;
1244 0 : _snes.destroy();
1245 0 : _setup_reuse = false;
1246 0 : }
1247 :
1248 : //------------------------------------------------------------------
1249 : // Explicit instantiations
1250 : template class LIBMESH_EXPORT PetscNonlinearSolver<Number>;
1251 :
1252 : } // namespace libMesh
1253 :
1254 :
1255 :
1256 : #endif // #ifdef LIBMESH_HAVE_PETSC
|