LCOV - code coverage report
Current view: top level - src/systems - nonlinear_implicit_system.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4411 (aefcbc) with base 893689 Lines: 77 148 52.0 %
Date: 2026-07-27 16:32:15 Functions: 6 11 54.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // The libMesh Finite Element Library.
       2             : // Copyright (C) 2002-2026 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             : // Local includes
      21             : #include "libmesh/nonlinear_implicit_system.h"
      22             : #include "libmesh/diff_solver.h"
      23             : #include "libmesh/equation_systems.h"
      24             : #include "libmesh/libmesh_logging.h"
      25             : #include "libmesh/mesh_tools.h"
      26             : #include "libmesh/nonlinear_solver.h"
      27             : #include "libmesh/sparse_matrix.h"
      28             : #include "libmesh/static_condensation.h"
      29             : #include "libmesh/static_condensation_preconditioner.h"
      30             : 
      31             : namespace libMesh
      32             : {
      33             : 
      34        1470 : NonlinearImplicitSystem::NonlinearImplicitSystem (EquationSystems & es,
      35             :                                                   const std::string & name_in,
      36        1470 :                                                   const unsigned int number_in) :
      37             : 
      38             :   Parent                    (es, name_in, number_in),
      39        1386 :   nonlinear_solver          (NonlinearSolver<Number>::build(*this)),
      40        1386 :   diff_solver               (),
      41        1386 :   _n_nonlinear_iterations   (0),
      42        1470 :   _final_nonlinear_residual (1.e20)
      43             : {
      44             :   // Set default parameters
      45             :   // These were chosen to match the Petsc defaults
      46        1470 :   es.parameters.set<Real>        ("linear solver tolerance") = 1e-5;
      47        1470 :   es.parameters.set<Real>        ("linear solver minimum tolerance") = 1e-5;
      48        1470 :   es.parameters.set<unsigned int>("linear solver maximum iterations") = 10000;
      49             : 
      50        1470 :   es.parameters.set<unsigned int>("nonlinear solver maximum iterations") = 50;
      51        1470 :   es.parameters.set<unsigned int>("nonlinear solver maximum function evaluations") = 10000;
      52             : 
      53        1470 :   es.parameters.set<Real>("nonlinear solver absolute residual tolerance") = 1e-35;
      54        1470 :   es.parameters.set<Real>("nonlinear solver relative residual tolerance") = 1e-8;
      55        1470 :   es.parameters.set<Real>("nonlinear solver divergence tolerance") = 1e+4;
      56        1470 :   es.parameters.set<Real>("nonlinear solver absolute step tolerance") = 1e-8;
      57        1470 :   es.parameters.set<Real>("nonlinear solver relative step tolerance") = 1e-8;
      58             : 
      59        1470 :   es.parameters.set<bool>("reuse preconditioner") = false;
      60        1470 :   es.parameters.set<unsigned int>("reuse preconditioner maximum linear iterations") = 1;
      61             : 
      62        1470 :   if (this->has_static_condensation())
      63         280 :     this->setup_static_condensation_preconditioner(*nonlinear_solver);
      64        1470 : }
      65             : 
      66             : 
      67             : 
      68        2508 : NonlinearImplicitSystem::~NonlinearImplicitSystem () = default;
      69             : 
      70             : 
      71             : 
      72           0 : void NonlinearImplicitSystem::create_static_condensation()
      73             : {
      74           0 :   Parent::create_static_condensation();
      75           0 :   this->setup_static_condensation_preconditioner(*nonlinear_solver);
      76           0 : }
      77             : 
      78             : 
      79             : 
      80         280 : void NonlinearImplicitSystem::clear ()
      81             : {
      82             :   // clear the nonlinear solver
      83         280 :   nonlinear_solver->clear();
      84             : 
      85             :   // FIXME - this is necessary for petsc_auto_fieldsplit
      86             :   // nonlinear_solver->init_names(*this);
      87             : 
      88             :   // clear the parent data
      89         280 :   Parent::clear();
      90             : 
      91             :   // And restore any StaticCondensation to defaults
      92         280 :   if (this->has_static_condensation())
      93           0 :     this->setup_static_condensation_preconditioner(*nonlinear_solver);
      94         280 : }
      95             : 
      96             : 
      97             : 
      98           0 : void NonlinearImplicitSystem::reinit ()
      99             : {
     100             :   // re-initialize the nonlinear solver interface
     101           0 :   nonlinear_solver->clear();
     102             : 
     103             :   // force the solver to get a new preconditioner, in
     104             :   // case reuse was set
     105           0 :   nonlinear_solver->force_new_preconditioner();
     106             : 
     107             :   // FIXME - this is necessary for petsc_auto_fieldsplit
     108             :   // nonlinear_solver->init_names(*this);
     109             : 
     110           0 :   if (diff_solver.get())
     111           0 :     diff_solver->reinit();
     112             : 
     113             :   // initialize parent data
     114           0 :   Parent::reinit();
     115           0 : }
     116             : 
     117             : 
     118             : 
     119       29400 : void NonlinearImplicitSystem::set_solver_parameters ()
     120             : {
     121             :   // Get a reference to the EquationSystems
     122             :   const EquationSystems & es =
     123        1680 :     this->get_equation_systems();
     124             : 
     125             :   // Get the user-specified nonlinear solver tolerances
     126       57960 :   const unsigned int maxits = parameters.have_parameter<unsigned int>("nonlinear solver maximum iterations") ?
     127           0 :     parameters.get<unsigned int>("nonlinear solver maximum iterations") :
     128       57960 :     es.parameters.get<unsigned int>("nonlinear solver maximum iterations");
     129             : 
     130       57960 :   const unsigned int maxfuncs = parameters.have_parameter<unsigned int>("nonlinear solver maximum function evaluations") ?
     131           0 :     parameters.get<unsigned int>("nonlinear solver maximum function evaluations") :
     132       57960 :     es.parameters.get<unsigned int>("nonlinear solver maximum function evaluations");
     133             : 
     134       57960 :   const double abs_resid_tol = parameters.have_parameter<Real>("nonlinear solver absolute residual tolerance") ?
     135       27720 :     double(parameters.get<Real>("nonlinear solver absolute residual tolerance")) :
     136       57960 :     double(es.parameters.get<Real>("nonlinear solver absolute residual tolerance"));
     137             : 
     138       57960 :   const double rel_resid_tol = parameters.have_parameter<Real>("nonlinear solver relative residual tolerance") ?
     139           0 :     double(parameters.get<Real>("nonlinear solver relative residual tolerance")) :
     140       57960 :     double(es.parameters.get<Real>("nonlinear solver relative residual tolerance"));
     141             : 
     142       57960 :   const double div_tol = parameters.have_parameter<Real>("nonlinear solver divergence tolerance") ?
     143           0 :     double(parameters.get<Real>("nonlinear solver divergence tolerance")) :
     144       57960 :     double(es.parameters.get<Real>("nonlinear solver divergence tolerance"));
     145             : 
     146       57960 :   const double abs_step_tol = parameters.have_parameter<Real>("nonlinear solver absolute step tolerance") ?
     147           0 :     double(parameters.get<Real>("nonlinear solver absolute step tolerance")) :
     148       57960 :     double(es.parameters.get<Real>("nonlinear solver absolute step tolerance"));
     149             : 
     150       57960 :   const double rel_step_tol = parameters.have_parameter<Real>("nonlinear solver relative step tolerance")?
     151           0 :     double(parameters.get<Real>("nonlinear solver relative step tolerance")) :
     152       29400 :     double(es.parameters.get<Real>("nonlinear solver relative step tolerance"));
     153             : 
     154             :   // Get the user-specified linear solver tolerances
     155       29400 :   const auto [maxlinearits, linear_tol] = this->Parent::get_linear_solve_parameters();
     156             : 
     157       57960 :   const double linear_min_tol = parameters.have_parameter<Real>("linear solver minimum tolerance") ?
     158           0 :     double(parameters.get<Real>("linear solver minimum tolerance")) :
     159       57960 :     double(es.parameters.get<Real>("linear solver minimum tolerance"));
     160             : 
     161       29400 :   const bool reuse_preconditioner = parameters.have_parameter<unsigned int>("reuse preconditioner") ?
     162           0 :     parameters.get<unsigned int>("reuse preconditioner") :
     163       29400 :       es.parameters.get<bool>("reuse preconditioner");
     164             :   const unsigned int reuse_preconditioner_max_linear_its =
     165       57960 :     parameters.have_parameter<unsigned int>("reuse preconditioner maximum linear iterations") ?
     166           0 :     parameters.get<unsigned int>("reuse preconditioner maximum linear iterations") :
     167       57960 :       es.parameters.get<unsigned int>("reuse preconditioner maximum linear iterations");
     168             : 
     169             :   // Set all the parameters on the NonlinearSolver
     170       29400 :   nonlinear_solver->max_nonlinear_iterations = maxits;
     171       29400 :   nonlinear_solver->max_function_evaluations = maxfuncs;
     172       29400 :   nonlinear_solver->absolute_residual_tolerance = abs_resid_tol;
     173       29400 :   nonlinear_solver->relative_residual_tolerance = rel_resid_tol;
     174       29400 :   nonlinear_solver->divergence_tolerance = div_tol;
     175       29400 :   nonlinear_solver->absolute_step_tolerance = abs_step_tol;
     176       29400 :   nonlinear_solver->relative_step_tolerance = rel_step_tol;
     177       29400 :   nonlinear_solver->max_linear_iterations = maxlinearits;
     178       29400 :   nonlinear_solver->initial_linear_tolerance = linear_tol;
     179       29400 :   nonlinear_solver->minimum_linear_tolerance = linear_min_tol;
     180       29400 :   nonlinear_solver->set_reuse_preconditioner(reuse_preconditioner);
     181       29400 :   nonlinear_solver->set_reuse_preconditioner_max_linear_its(reuse_preconditioner_max_linear_its);
     182             : 
     183       29400 :   if (diff_solver.get())
     184             :     {
     185           0 :       diff_solver->max_nonlinear_iterations = maxits;
     186           0 :       diff_solver->absolute_residual_tolerance = abs_resid_tol;
     187           0 :       diff_solver->relative_residual_tolerance = rel_resid_tol;
     188           0 :       diff_solver->absolute_step_tolerance = abs_step_tol;
     189           0 :       diff_solver->relative_step_tolerance = rel_step_tol;
     190           0 :       diff_solver->max_linear_iterations = maxlinearits;
     191           0 :       diff_solver->initial_linear_tolerance = linear_tol;
     192           0 :       diff_solver->minimum_linear_tolerance = linear_min_tol;
     193             :     }
     194       29400 : }
     195             : 
     196             : 
     197             : 
     198       29400 : void NonlinearImplicitSystem::solve ()
     199             : {
     200             :   // Log how long the nonlinear solve takes.
     201        1680 :   LOG_SCOPE("solve()", "System");
     202             : 
     203       29400 :   this->set_solver_parameters();
     204             : 
     205       29400 :   if (diff_solver.get())
     206             :     {
     207           0 :       diff_solver->solve();
     208             : 
     209             :       // Store the number of nonlinear iterations required to
     210             :       // solve and the final residual.
     211           0 :       _n_nonlinear_iterations   = diff_solver->total_outer_iterations();
     212           0 :       _final_nonlinear_residual = 0.; // FIXME - support this!
     213             :     }
     214             :   else
     215             :     {
     216       29400 :       if (this->prefix_with_name())
     217           0 :         nonlinear_solver->init(this->prefix().c_str());
     218             :       else
     219       29400 :         nonlinear_solver->init();
     220             : 
     221             :       // FIXME - this is necessary for petsc_auto_fieldsplit
     222             :       // nonlinear_solver->init_names(*this);
     223             : 
     224             :       // Solve the nonlinear system.
     225             :       // Store the number of nonlinear iterations required to
     226             :       // solve and the final residual.
     227       29400 :       std::tie(_n_nonlinear_iterations, _final_nonlinear_residual) =
     228       30240 :         nonlinear_solver->solve (*matrix, *solution, *rhs,
     229         840 :                                  nonlinear_solver->relative_residual_tolerance,
     230        3360 :                                  nonlinear_solver->max_linear_iterations);
     231             :     }
     232             : 
     233             :   // Update the system after the solve
     234       29400 :   this->update();
     235       29400 : }
     236             : 
     237             : 
     238             : 
     239           0 : std::pair<unsigned int, Real> NonlinearImplicitSystem::get_linear_solve_parameters() const
     240             : {
     241           0 :   if (diff_solver.get())
     242           0 :     return std::make_pair(this->diff_solver->max_linear_iterations,
     243           0 :                           this->diff_solver->relative_residual_tolerance);
     244           0 :   return std::make_pair(this->nonlinear_solver->max_linear_iterations,
     245           0 :                         this->nonlinear_solver->relative_residual_tolerance);
     246             : }
     247             : 
     248             : 
     249             : 
     250           0 : void NonlinearImplicitSystem::assembly(bool get_residual,
     251             :                                        bool get_jacobian,
     252             :                                        bool /*apply_heterogeneous_constraints*/,
     253             :                                        bool /*apply_no_constraints*/)
     254             : {
     255           0 :   libmesh_assert(this->get_mesh().is_prepared());
     256             : #if defined(DEBUG) && !defined(LIBMESH_ENABLE_DEPRECATED)
     257             :   MeshTools::libmesh_assert_valid_is_prepared(this->get_mesh());
     258             : #endif
     259             : 
     260             :   // Get current_local_solution in sync
     261           0 :   this->update();
     262             : 
     263             :   //-----------------------------------------------------------------------------
     264             :   // if the user has provided both function pointers and objects only the pointer
     265             :   // will be used, so catch that as an error
     266           0 :   libmesh_error_msg_if(nonlinear_solver->jacobian && nonlinear_solver->jacobian_object,
     267             :                        "ERROR: cannot specify both a function and object to compute the Jacobian!");
     268             : 
     269           0 :   libmesh_error_msg_if(nonlinear_solver->residual && nonlinear_solver->residual_object,
     270             :                        "ERROR: cannot specify both a function and object to compute the Residual!");
     271             : 
     272           0 :   libmesh_error_msg_if(nonlinear_solver->matvec && nonlinear_solver->residual_and_jacobian_object,
     273             :                        "ERROR: cannot specify both a function and object to compute the combined Residual & Jacobian!");
     274             : 
     275             : 
     276           0 :   if (get_jacobian)
     277             :     {
     278           0 :       if (nonlinear_solver->jacobian != nullptr)
     279           0 :         nonlinear_solver->jacobian (*current_local_solution.get(), *matrix, *this);
     280             : 
     281           0 :       else if (nonlinear_solver->jacobian_object != nullptr)
     282           0 :         nonlinear_solver->jacobian_object->jacobian (*current_local_solution.get(), *matrix, *this);
     283             : 
     284           0 :       else if (nonlinear_solver->matvec != nullptr)
     285           0 :         nonlinear_solver->matvec (*current_local_solution.get(), get_residual ? rhs : nullptr, matrix, *this);
     286             : 
     287           0 :       else if (nonlinear_solver->residual_and_jacobian_object != nullptr)
     288           0 :         nonlinear_solver->residual_and_jacobian_object->residual_and_jacobian (*current_local_solution.get(), get_residual ? rhs : nullptr, matrix, *this);
     289             : 
     290             :       else
     291           0 :         libmesh_error_msg("Error! Unable to compute residual and/or Jacobian!");
     292             :     }
     293             : 
     294           0 :   if (get_residual)
     295             :     {
     296           0 :       if (nonlinear_solver->residual != nullptr)
     297           0 :         nonlinear_solver->residual (*current_local_solution.get(), *rhs, *this);
     298             : 
     299           0 :       else if (nonlinear_solver->residual_object != nullptr)
     300           0 :         nonlinear_solver->residual_object->residual (*current_local_solution.get(), *rhs, *this);
     301             : 
     302           0 :       else if (nonlinear_solver->matvec != nullptr)
     303             :         {
     304             :           // we might have already grabbed the residual and jacobian together
     305           0 :           if (!get_jacobian)
     306           0 :             nonlinear_solver->matvec (*current_local_solution.get(), rhs, nullptr, *this);
     307             :         }
     308             : 
     309           0 :       else if (nonlinear_solver->residual_and_jacobian_object != nullptr)
     310             :         {
     311             :           // we might have already grabbed the residual and jacobian together
     312           0 :           if (!get_jacobian)
     313           0 :             nonlinear_solver->residual_and_jacobian_object->residual_and_jacobian (*current_local_solution.get(), rhs, nullptr, *this);
     314             :         }
     315             : 
     316             :       else
     317           0 :         libmesh_error_msg("Error! Unable to compute residual and/or Jacobian!");
     318             :     }
     319             :   else
     320           0 :     libmesh_assert(get_jacobian);  // I can't believe you really wanted to assemble *nothing*
     321           0 : }
     322             : 
     323             : 
     324             : 
     325             : 
     326           0 : unsigned NonlinearImplicitSystem::get_current_nonlinear_iteration_number() const
     327             : {
     328           0 :   return nonlinear_solver->get_current_nonlinear_iteration_number();
     329             : }
     330             : 
     331             : 
     332             : 
     333             : } // namespace libMesh

Generated by: LCOV version 1.14