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