LCOV - code coverage report
Current view: top level - src/systems - continuation_system.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4229 (6a9aeb) with base 727f46 Lines: 0 464 0.0 %
Date: 2025-08-19 19:27:09 Functions: 0 17 0.0 %
Legend: Lines: hit not hit

          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             : // LibMesh includes
      19             : #include "libmesh/equation_systems.h"
      20             : #include "libmesh/continuation_system.h"
      21             : #include "libmesh/linear_solver.h"
      22             : #include "libmesh/time_solver.h"
      23             : #include "libmesh/newton_solver.h"
      24             : #include "libmesh/numeric_vector.h"
      25             : #include "libmesh/sparse_matrix.h"
      26             : 
      27             : namespace libMesh
      28             : {
      29             : 
      30           0 : ContinuationSystem::ContinuationSystem (EquationSystems & es,
      31             :                                         const std::string & name_in,
      32           0 :                                         const unsigned int number_in) :
      33             :   Parent(es, name_in, number_in),
      34           0 :   continuation_parameter(nullptr),
      35           0 :   quiet(true),
      36           0 :   continuation_parameter_tolerance(1.e-6),
      37           0 :   solution_tolerance(1.e-6),
      38           0 :   initial_newton_tolerance(0.01),
      39           0 :   old_continuation_parameter(0.),
      40           0 :   min_continuation_parameter(0.),
      41           0 :   max_continuation_parameter(0.),
      42           0 :   Theta(1.),
      43           0 :   Theta_LOCA(1.),
      44           0 :   n_backtrack_steps(5),
      45           0 :   n_arclength_reductions(5),
      46           0 :   ds_min(1.e-8),
      47           0 :   predictor(Euler),
      48           0 :   newton_stepgrowth_aggressiveness(1.),
      49           0 :   newton_progress_check(true),
      50           0 :   rhs_mode(Residual),
      51           0 :   tangent_initialized(false),
      52           0 :   newton_solver(nullptr),
      53           0 :   dlambda_ds(0.707),
      54           0 :   ds(0.1),
      55           0 :   ds_current(0.1),
      56           0 :   previous_dlambda_ds(0.),
      57           0 :   previous_ds(0.),
      58           0 :   newton_step(0)
      59             : {
      60             :   // Warn about using untested code
      61             :   libmesh_experimental();
      62             : 
      63             :   // linear_solver is now in the ImplicitSystem base class, but we are
      64             :   // going to keep using it basically the way we did before it was
      65             :   // moved.
      66           0 :   linear_solver = LinearSolver<Number>::build(es.comm());
      67             : 
      68           0 :   if (this->prefix_with_name())
      69           0 :     linear_solver->init(this->prefix().c_str());
      70             :   else
      71           0 :     linear_solver->init();
      72             : 
      73           0 :   linear_solver->init_names(*this);
      74           0 : }
      75             : 
      76             : 
      77             : 
      78             : 
      79           0 : ContinuationSystem::~ContinuationSystem () = default;
      80             : 
      81             : 
      82             : 
      83           0 : void ContinuationSystem::clear()
      84             : {
      85             :   // Call the Parent's clear function
      86           0 :   Parent::clear();
      87           0 : }
      88             : 
      89             : 
      90             : 
      91           0 : void ContinuationSystem::init_data ()
      92             : {
      93             :   // Add a vector which stores the tangent "du/ds" to the system and save its pointer.
      94           0 :   du_ds = &(add_vector("du_ds"));
      95             : 
      96             :   // Add a vector which stores the tangent "du/ds" to the system and save its pointer.
      97           0 :   previous_du_ds = &(add_vector("previous_du_ds"));
      98             : 
      99             :   // Add a vector to keep track of the previous nonlinear solution
     100             :   // at the old value of lambda.
     101           0 :   previous_u = &(add_vector("previous_u"));
     102             : 
     103             :   // Add a vector to keep track of the temporary solution "y" of Ay=G_{\lambda}.
     104           0 :   y = &(add_vector("y"));
     105             : 
     106             :   // Add a vector to keep track of the "old value" of "y" which is the solution of Ay=G_{\lambda}.
     107           0 :   y_old = &(add_vector("y_old"));
     108             : 
     109             :   // Add a vector to keep track of the temporary solution "z" of Az=-G.
     110           0 :   z = &(add_vector("z"));
     111             : 
     112             :   // Add a vector to keep track of the Newton update during the constrained PDE solves.
     113           0 :   delta_u = &(add_vector("delta_u"));
     114             : 
     115             :   // Call the Parent's initialization routine.
     116           0 :   Parent::init_data();
     117           0 : }
     118             : 
     119             : 
     120             : 
     121             : 
     122           0 : void ContinuationSystem::solve()
     123             : {
     124             :   // Set the Residual RHS mode, and call the normal solve routine.
     125           0 :   rhs_mode      = Residual;
     126           0 :   DifferentiableSystem::solve();
     127           0 : }
     128             : 
     129             : 
     130             : 
     131             : 
     132           0 : void ContinuationSystem::initialize_tangent()
     133             : {
     134             :   // Be sure the tangent was not already initialized.
     135           0 :   libmesh_assert (!tangent_initialized);
     136             : 
     137             :   // Compute delta_s_zero, the initial arclength traveled during the
     138             :   // first step.  Here we assume that previous_u and lambda_old store
     139             :   // the previous solution and control parameter.  You may need to
     140             :   // read in an old solution (or solve the non-continuation system)
     141             :   // first and call save_current_solution() before getting here.
     142             : 
     143             :   // 1.) Compute delta_s_zero as ||u|| - ||u_old|| + ...
     144             :   // Compute norms of the current and previous solutions
     145             :   //   Real norm_u          = solution->l2_norm();
     146             :   //   Real norm_previous_u = previous_u->l2_norm();
     147             : 
     148             :   //   if (!quiet)
     149             :   //     {
     150             :   //       libMesh::out << "norm_u=" << norm_u << std::endl;
     151             :   //       libMesh::out << "norm_previous_u=" << norm_previous_u << std::endl;
     152             :   //     }
     153             : 
     154             :   //   if (norm_u == norm_previous_u)
     155             :   //     {
     156             :   //       libMesh::err << "Warning, it appears u and previous_u are the "
     157             :   //   << "same, are you sure this is correct?"
     158             :   //   << "It's possible you forgot to set one or the other..."
     159             :   //   << std::endl;
     160             :   //     }
     161             : 
     162             :   //   Real delta_s_zero = std::sqrt(
     163             :   //   (norm_u - norm_previous_u)*(norm_u - norm_previous_u) +
     164             :   //   (*continuation_parameter-old_continuation_parameter)*
     165             :   //   (*continuation_parameter-old_continuation_parameter)
     166             :   //   );
     167             : 
     168             :   //   // 2.) Compute delta_s_zero as ||u -u_old|| + ...
     169             :   //   *delta_u = *solution;
     170             :   //   delta_u->add(-1., *previous_u);
     171             :   //   delta_u->close();
     172             :   //   Real norm_delta_u = delta_u->l2_norm();
     173             :   //   Real norm_u          = solution->l2_norm();
     174             :   //   Real norm_previous_u = previous_u->l2_norm();
     175             : 
     176             :   //   // Scale norm_delta_u by the bigger of either norm_u or norm_previous_u
     177             :   //   norm_delta_u /= std::max(norm_u, norm_previous_u);
     178             : 
     179             :   //   if (!quiet)
     180             :   //     {
     181             :   //       libMesh::out << "norm_u=" << norm_u << std::endl;
     182             :   //       libMesh::out << "norm_previous_u=" << norm_previous_u << std::endl;
     183             :   //       //libMesh::out << "norm_delta_u=" << norm_delta_u << std::endl;
     184             :   //       libMesh::out << "norm_delta_u/max(|u|,|u_old|)=" << norm_delta_u << std::endl;
     185             :   //       libMesh::out << "|norm_u-norm_previous_u|=" << std::abs(norm_u - norm_previous_u) << std::endl;
     186             :   //     }
     187             : 
     188             :   //   const Real dlambda = *continuation_parameter-old_continuation_parameter;
     189             : 
     190             :   //   if (!quiet)
     191             :   //     libMesh::out << "dlambda=" << dlambda << std::endl;
     192             : 
     193             :   //   Real delta_s_zero = std::sqrt(
     194             :   //   (norm_delta_u*norm_delta_u) +
     195             :   //   (dlambda*dlambda)
     196             :   //   );
     197             : 
     198             :   //   if (!quiet)
     199             :   //     libMesh::out << "delta_s_zero=" << delta_s_zero << std::endl;
     200             : 
     201             :   // 1.) + 2.)
     202             :   //   // Now approximate the initial tangent d(lambda)/ds
     203             :   //   this->dlambda_ds = (*continuation_parameter-old_continuation_parameter) / delta_s_zero;
     204             : 
     205             : 
     206             :   //   // We can also approximate the deriv. wrt s by finite differences:
     207             :   //   // du/ds = (u1 - u0) / delta_s_zero.
     208             :   //   // FIXME: Use delta_u from above if we decide to keep that method.
     209             :   //   *du_ds = *solution;
     210             :   //   du_ds->add(-1., *previous_u);
     211             :   //   du_ds->scale(1./delta_s_zero);
     212             :   //   du_ds->close();
     213             : 
     214             : 
     215             :   // 3.) Treating (u-previous_u)/(lambda - lambda_old) as an approximation to du/d(lambda),
     216             :   // we follow the same technique as Carnes and Shadid.
     217             :   //   const Real dlambda = *continuation_parameter-old_continuation_parameter;
     218             :   //   libmesh_assert_greater (dlambda, 0.);
     219             : 
     220             :   //   // Use delta_u for temporary calculation of du/d(lambda)
     221             :   //   *delta_u = *solution;
     222             :   //   delta_u->add(-1., *previous_u);
     223             :   //   delta_u->scale(1. / dlambda);
     224             :   //   delta_u->close();
     225             : 
     226             :   //   // Determine initial normalization parameter
     227             :   //   const Real solution_size = std::max(solution->l2_norm(), previous_u->l2_norm());
     228             :   //   if (solution_size > 1.)
     229             :   //     {
     230             :   //       Theta = 1./solution_size;
     231             : 
     232             :   //       if (!quiet)
     233             :   // libMesh::out << "Setting Normalization Parameter Theta=" << Theta << std::endl;
     234             :   //     }
     235             : 
     236             :   //   // Compute d(lambda)/ds
     237             :   //   // The correct sign of d(lambda)/ds should be positive, since we assume that (lambda > lambda_old)
     238             :   //   // but we could always double-check that as well.
     239             :   //   Real norm_delta_u = delta_u->l2_norm();
     240             :   //   this->dlambda_ds = 1. / std::sqrt(1. + Theta*Theta*norm_delta_u*norm_delta_u);
     241             : 
     242             :   //   // Finally, compute du/ds = d(lambda)/ds * du/d(lambda)
     243             :   //   *du_ds = *delta_u;
     244             :   //   du_ds->scale(dlambda_ds);
     245             :   //   du_ds->close();
     246             : 
     247             : 
     248             :   // 4.) Use normalized arclength formula to estimate delta_s_zero
     249             :   //   // Determine initial normalization parameter
     250             :   //   set_Theta();
     251             : 
     252             :   //   // Compute (normalized) delta_s_zero
     253             :   //   *delta_u = *solution;
     254             :   //   delta_u->add(-1., *previous_u);
     255             :   //   delta_u->close();
     256             :   //   Real norm_delta_u = delta_u->l2_norm();
     257             : 
     258             :   //   const Real dlambda = *continuation_parameter-old_continuation_parameter;
     259             : 
     260             :   //   if (!quiet)
     261             :   //     libMesh::out << "dlambda=" << dlambda << std::endl;
     262             : 
     263             :   //   Real delta_s_zero = std::sqrt(
     264             :   //   (Theta_LOCA*Theta_LOCA*Theta*norm_delta_u*norm_delta_u) +
     265             :   //   (dlambda*dlambda)
     266             :   //   );
     267             :   //   *du_ds = *delta_u;
     268             :   //   du_ds->scale(1./delta_s_zero);
     269             :   //   dlambda_ds = dlambda / delta_s_zero;
     270             : 
     271             :   //   if (!quiet)
     272             :   //     {
     273             :   //       libMesh::out << "delta_s_zero=" << delta_s_zero << std::endl;
     274             :   //       libMesh::out << "initial d(lambda)/ds|_0 = " << dlambda_ds << std::endl;
     275             :   //       libMesh::out << "initial ||du_ds||_0 = " << du_ds->l2_norm() << std::endl;
     276             :   //     }
     277             : 
     278             :   //   // FIXME: Also store the initial finite-differenced approximation to -du/dlambda as y.
     279             :   //   // We stick to the convention of storing negative y, since that is what we typically
     280             :   //   // solve for anyway.
     281             :   //   *y = *delta_u;
     282             :   //   y->scale(-1./dlambda);
     283             :   //   y->close();
     284             : 
     285             : 
     286             : 
     287             :   // 5.) Assume dlambda/ds_0 ~ 1/sqrt(2) and determine the value of Theta_LOCA which
     288             :   // will satisfy this criterion
     289             : 
     290             :   // Initial change in parameter
     291           0 :   const Real dlambda = *continuation_parameter-old_continuation_parameter;
     292           0 :   libmesh_assert_not_equal_to (dlambda, 0.0);
     293             : 
     294             :   // Ideal initial value of dlambda_ds
     295           0 :   dlambda_ds = 1. / std::sqrt(2.);
     296           0 :   if (dlambda < 0.)
     297           0 :     dlambda_ds *= -1.;
     298             : 
     299             :   // This also implies the initial value of ds
     300           0 :   ds_current = dlambda / dlambda_ds;
     301             : 
     302           0 :   if (!quiet)
     303           0 :     libMesh::out << "Setting ds_current|_0=" << ds_current << std::endl;
     304             : 
     305             :   // Set y = -du/dlambda using finite difference approximation
     306           0 :   *y = *solution;
     307           0 :   y->add(-1., *previous_u);
     308           0 :   y->scale(-1./dlambda);
     309           0 :   y->close();
     310           0 :   const Real ynorm=y->l2_norm();
     311             : 
     312             :   // Finally, set the value of du_ds to be used in the upcoming
     313             :   // tangent calculation. du/ds = du/dlambda * dlambda/ds
     314           0 :   *du_ds = *y;
     315           0 :   du_ds->scale(-dlambda_ds);
     316           0 :   du_ds->close();
     317             : 
     318             :   // Determine additional solution normalization parameter
     319             :   // (Since we just set du/ds, it will be:  ||du||*||du/ds||)
     320           0 :   set_Theta();
     321             : 
     322             :   // The value of Theta_LOCA which makes dlambda_ds = 1/sqrt(2),
     323             :   // assuming our Theta = ||du||^2.
     324             :   // Theta_LOCA = std::abs(dlambda);
     325             : 
     326             :   // Assuming general Theta
     327           0 :   Theta_LOCA = std::sqrt(1./Theta/ynorm/ynorm);
     328             : 
     329             : 
     330           0 :   if (!quiet)
     331             :     {
     332           0 :       libMesh::out << "Setting initial Theta_LOCA = " << Theta_LOCA << std::endl;
     333           0 :       libMesh::out << "Theta_LOCA^2*Theta         = " << Theta_LOCA*Theta_LOCA*Theta << std::endl;
     334           0 :       libMesh::out << "initial d(lambda)/ds|_0    = " << dlambda_ds << std::endl;
     335           0 :       libMesh::out << "initial ||du_ds||_0        = " << du_ds->l2_norm() << std::endl;
     336             :     }
     337             : 
     338             : 
     339             : 
     340             :   // OK, we estimated the tangent at point u0.
     341             :   // Now, to estimate the tangent at point u1, we call the solve_tangent routine.
     342             : 
     343             :   // Set the flag which tells us the method has been initialized.
     344           0 :   tangent_initialized = true;
     345             : 
     346           0 :   solve_tangent();
     347             : 
     348             :   // Advance the solution and the parameter to the next value.
     349           0 :   update_solution();
     350           0 : }
     351             : 
     352             : 
     353             : 
     354             : 
     355             : 
     356             : 
     357             : // This is most of the "guts" of this class.  This is where we implement
     358             : // our custom Newton iterations and perform most of the solves.
     359           0 : void ContinuationSystem::continuation_solve()
     360             : {
     361             :   // Be sure the user has set the continuation parameter pointer
     362           0 :   libmesh_error_msg_if(!continuation_parameter,
     363             :                        "You must set the continuation_parameter pointer "
     364             :                        "to a member variable of the derived class, preferably in the "
     365             :                        "Derived class's init_data function.  This is how the ContinuationSystem "
     366             :                        "updates the continuation parameter.");
     367             : 
     368             :   // Use extra precision for all the numbers printed in this function.
     369           0 :   std::streamsize old_precision = libMesh::out.precision();
     370           0 :   libMesh::out.precision(16);
     371           0 :   libMesh::out.setf(std::ios_base::scientific);
     372             : 
     373             :   // We can't start solving the augmented PDE system unless the tangent
     374             :   // vectors have been initialized.  This only needs to occur once.
     375           0 :   if (!tangent_initialized)
     376           0 :     initialize_tangent();
     377             : 
     378             :   // Save the old value of -du/dlambda.  This will be used after the Newton iterations
     379             :   // to compute the angle between previous tangent vectors.  This cosine of this angle is
     380             :   //
     381             :   // tau := abs( (du/d(lambda)_i , du/d(lambda)_{i-1}) / (||du/d(lambda)_i|| * ||du/d(lambda)_{i-1}||) )
     382             :   //
     383             :   // The scaling factor tau (which should vary between 0 and 1) is used to shrink the step-size ds
     384             :   // when we are approaching a turning point.  Note that it can only shrink the step size.
     385           0 :   *y_old = *y;
     386             : 
     387             :   // Set pointer to underlying Newton solver
     388           0 :   if (!newton_solver)
     389           0 :     newton_solver = cast_ptr<NewtonSolver *> (this->time_solver->diff_solver().get());
     390             : 
     391             :   // A pair for catching return values from linear system solves.
     392           0 :   std::pair<unsigned int, Real> rval;
     393             : 
     394             :   // Convergence flag for the entire arcstep
     395           0 :   bool arcstep_converged = false;
     396             : 
     397             :   // Begin loop over arcstep reductions.
     398           0 :   for (unsigned int ns=0; ns<n_arclength_reductions; ++ns)
     399             :     {
     400           0 :       if (!quiet)
     401             :         {
     402           0 :           libMesh::out << "Current arclength stepsize, ds_current=" << ds_current << std::endl;
     403           0 :           libMesh::out << "Current parameter value, lambda=" << *continuation_parameter << std::endl;
     404             :         }
     405             : 
     406             :       // Upon exit from the nonlinear loop, the newton_converged flag
     407             :       // will tell us the convergence status of Newton's method.
     408           0 :       bool newton_converged = false;
     409             : 
     410             :       // The nonlinear residual before *any* nonlinear steps have been taken.
     411           0 :       Real nonlinear_residual_firststep = 0.;
     412             : 
     413             :       // The nonlinear residual from the current "k" Newton step, before the Newton step
     414           0 :       Real nonlinear_residual_beforestep = 0.;
     415             : 
     416             :       // The nonlinear residual from the current "k" Newton step, after the Newton step
     417           0 :       Real nonlinear_residual_afterstep = 0.;
     418             : 
     419             :       // The linear solver tolerance, can be updated dynamically at each Newton step.
     420           0 :       double current_linear_tolerance = 0.;
     421             : 
     422             :       // The nonlinear loop
     423           0 :       for (newton_step=0; newton_step<newton_solver->max_nonlinear_iterations; ++newton_step)
     424             :         {
     425           0 :           libMesh::out << "\n === Starting Newton step " << newton_step << " ===" << std::endl;
     426             : 
     427             :           // Set the linear system solver tolerance
     428             :           //   // 1.) Set the current linear tolerance based as a multiple of the current residual of the system.
     429             :           //   const Real residual_multiple = 1.e-4;
     430             :           //   Real current_linear_tolerance = residual_multiple*nonlinear_residual_beforestep;
     431             : 
     432             :           //   // But if the current residual isn't small, don't let the solver exit with zero iterations!
     433             :           //   if (current_linear_tolerance > 1.)
     434             :           //     current_linear_tolerance = residual_multiple;
     435             : 
     436             :           // 2.) Set the current linear tolerance based on the method based on technique of Eisenstat & Walker.
     437           0 :           if (newton_step==0)
     438             :             {
     439             :               // At first step, only try reducing the residual by a small amount
     440           0 :               current_linear_tolerance = initial_newton_tolerance;//0.01;
     441             :             }
     442             : 
     443             :           else
     444             :             {
     445             :               // The new tolerance is based on the ratio of the most recent tolerances
     446           0 :               const Real alp=0.5*(1.+std::sqrt(5.));
     447           0 :               const Real gam=0.9;
     448             : 
     449           0 :               libmesh_assert_not_equal_to (nonlinear_residual_beforestep, 0.0);
     450           0 :               libmesh_assert_not_equal_to (nonlinear_residual_afterstep, 0.0);
     451             : 
     452           0 :               current_linear_tolerance =
     453           0 :                 double(std::min(gam*std::pow(nonlinear_residual_afterstep/nonlinear_residual_beforestep, alp),
     454           0 :                                 current_linear_tolerance*current_linear_tolerance));
     455             : 
     456             :               // Don't let it get ridiculously small!!
     457           0 :               if (current_linear_tolerance < 1.e-12)
     458           0 :                 current_linear_tolerance = 1.e-12;
     459             :             }
     460             : 
     461           0 :           if (!quiet)
     462           0 :             libMesh::out << "Using current_linear_tolerance=" << current_linear_tolerance << std::endl;
     463             : 
     464             : 
     465             :           // Assemble the residual (and Jacobian).
     466           0 :           rhs_mode = Residual;
     467           0 :           assembly(true,   // Residual
     468           0 :                    true); // Jacobian
     469           0 :           rhs->close();
     470             : 
     471             :           // Save the current nonlinear residual.  We don't need to recompute the residual unless
     472             :           // this is the first step, since it was already computed as part of the convergence check
     473             :           // at the end of the last loop iteration.
     474           0 :           if (newton_step==0)
     475             :             {
     476           0 :               nonlinear_residual_beforestep = rhs->l2_norm();
     477             : 
     478             :               // Store the residual before any steps have been taken.  This will *not*
     479             :               // be updated at each step, and can be used to see if any progress has
     480             :               // been made from the initial residual at later steps.
     481           0 :               nonlinear_residual_firststep = nonlinear_residual_beforestep;
     482             : 
     483           0 :               const Real old_norm_u = solution->l2_norm();
     484           0 :               libMesh::out << "  (before step) ||R||_{L2} = " << nonlinear_residual_beforestep << std::endl;
     485           0 :               libMesh::out << "  (before step) ||R||_{L2}/||u|| = " << nonlinear_residual_beforestep / old_norm_u << std::endl;
     486             : 
     487             :               // In rare cases (very small arcsteps), it's possible that the residual is
     488             :               // already below our absolute linear tolerance.
     489           0 :               if (nonlinear_residual_beforestep  < solution_tolerance)
     490             :                 {
     491           0 :                   if (!quiet)
     492           0 :                     libMesh::out << "Initial guess satisfied linear tolerance, exiting with zero Newton iterations!" << std::endl;
     493             : 
     494             :                   // Since we go straight from here to the solve of the next tangent, we
     495             :                   // have to close the matrix before it can be assembled again.
     496           0 :                   matrix->close();
     497           0 :                   newton_converged=true;
     498           0 :                   break; // out of Newton iterations, with newton_converged=true
     499             :                 }
     500             :             }
     501             : 
     502             :           else
     503             :             {
     504           0 :               nonlinear_residual_beforestep = nonlinear_residual_afterstep;
     505             :             }
     506             : 
     507             : 
     508             :           // Solve the linear system G_u*z = G
     509             :           // Initial guess?
     510           0 :           z->zero(); // It seems to be extremely important to zero z here, otherwise the solver quits early.
     511           0 :           z->close();
     512             : 
     513             :           // It's possible that we have selected the current_linear_tolerance so large that
     514             :           // a guess of z=zero yields a linear system residual |Az + R| small enough that the
     515             :           // linear solver exits in zero iterations.  If this happens, we will reduce the
     516             :           // current_linear_tolerance until the linear solver does at least 1 iteration.
     517           0 :           do
     518             :             {
     519             :               rval =
     520           0 :                 linear_solver->solve(*matrix,
     521           0 :                                      *z,
     522           0 :                                      *rhs,
     523             :                                      //1.e-12,
     524             :                                      current_linear_tolerance,
     525           0 :                                      newton_solver->max_linear_iterations);   // max linear iterations
     526             : 
     527           0 :               if (rval.first==0)
     528             :                 {
     529           0 :                   if (newton_step==0)
     530             :                     {
     531           0 :                       libMesh::out << "Repeating initial solve with smaller linear tolerance!" << std::endl;
     532           0 :                       current_linear_tolerance *= initial_newton_tolerance; // reduce the linear tolerance to force the solver to do some work
     533             :                     }
     534             :                   else
     535             :                     // We shouldn't get here ... it means the linear solver did no work on a Newton
     536             :                     // step other than the first one.  If this happens, we need to think more about our
     537             :                     // tolerance selection.
     538           0 :                     libmesh_error_msg("Linear solver did no work!");
     539             :                 }
     540             : 
     541           0 :             } while (rval.first==0);
     542             : 
     543             : 
     544           0 :           if (!quiet)
     545           0 :             libMesh::out << "  G_u*z = G solver converged at step "
     546           0 :                          << rval.first
     547           0 :                          << " linear tolerance = "
     548           0 :                          << rval.second
     549           0 :                          << "."
     550           0 :                          << std::endl;
     551             : 
     552             :           // Sometimes (I am not sure why) the linear solver exits after zero iterations.
     553             :           // Perhaps it is hitting PETSc's divergence tolerance dtol???  If this occurs,
     554             :           // we should break out of the Newton iteration loop because nothing further is
     555             :           // going to happen...  Of course if the tolerance is already small enough after
     556             :           // zero iterations (how can this happen?!) we should not quit.
     557           0 :           if ((rval.first == 0) && (rval.second > current_linear_tolerance*nonlinear_residual_beforestep))
     558             :             {
     559           0 :               if (!quiet)
     560           0 :                 libMesh::out << "Linear solver exited in zero iterations!" << std::endl;
     561             : 
     562             :               // Try to find out the reason for convergence/divergence
     563           0 :               linear_solver->print_converged_reason();
     564             : 
     565           0 :               break; // out of Newton iterations
     566             :             }
     567             : 
     568             :           // Note: need to scale z by -1 since our code always solves Jx=R
     569             :           // instead of Jx=-R.
     570           0 :           z->scale(-1.);
     571           0 :           z->close();
     572             : 
     573             : 
     574             : 
     575             : 
     576             : 
     577             : 
     578             :           // Assemble the G_Lambda vector, skip residual.
     579           0 :           rhs_mode = G_Lambda;
     580             : 
     581             :           // Assemble both rhs and Jacobian
     582           0 :           assembly(true,  // Residual
     583           0 :                    false); // Jacobian
     584             : 
     585             :           // Not sure if this is really necessary
     586           0 :           rhs->close();
     587           0 :           const Real yrhsnorm=rhs->l2_norm();
     588           0 :           libmesh_error_msg_if(yrhsnorm == 0.0, "||G_Lambda|| = 0");
     589             : 
     590             :           // We select a tolerance for the y-system which is based on the inexact Newton
     591             :           // tolerance but scaled by an extra term proportional to the RHS (which is not -> 0 in this case)
     592           0 :           const double ysystemtol =
     593           0 :             double(current_linear_tolerance*(nonlinear_residual_beforestep/yrhsnorm));
     594           0 :           if (!quiet)
     595           0 :             libMesh::out << "ysystemtol=" << ysystemtol << std::endl;
     596             : 
     597             :           // Solve G_u*y = G_{\lambda}
     598             :           // FIXME: Initial guess?  This is really a solve for -du/dlambda so we could try
     599             :           // initializing it with the latest approximation to that... du/dlambda ~ du/ds * ds/dlambda
     600             :           //*y = *solution;
     601             :           //y->add(-1., *previous_u);
     602             :           //y->scale(-1. / (*continuation_parameter - old_continuation_parameter)); // Be careful of divide by zero...
     603             :           //y->close();
     604             : 
     605             :           //  const unsigned int max_attempts=1;
     606             :           // unsigned int attempt=0;
     607             :           //   do
     608             :           //     {
     609             :           //       if (!quiet)
     610             :           // libMesh::out << "Trying to solve tangent system, attempt " << attempt << std::endl;
     611             : 
     612             :           rval =
     613           0 :             linear_solver->solve(*matrix,
     614           0 :                                  *y,
     615           0 :                                  *rhs,
     616             :                                  //1.e-12,
     617             :                                  ysystemtol,
     618           0 :                                  newton_solver->max_linear_iterations);   // max linear iterations
     619             : 
     620           0 :           if (!quiet)
     621           0 :             libMesh::out << "  G_u*y = G_{lambda} solver converged at step "
     622           0 :                          << rval.first
     623           0 :                          << ", linear tolerance = "
     624           0 :                          << rval.second
     625           0 :                          << "."
     626           0 :                          << std::endl;
     627             : 
     628             :           // Sometimes (I am not sure why) the linear solver exits after zero iterations.
     629             :           // Perhaps it is hitting PETSc's divergence tolerance dtol???  If this occurs,
     630             :           // we should break out of the Newton iteration loop because nothing further is
     631             :           // going to happen...
     632           0 :           if ((rval.first == 0) && (rval.second > ysystemtol))
     633             :             {
     634           0 :               if (!quiet)
     635           0 :                 libMesh::out << "Linear solver exited in zero iterations!" << std::endl;
     636             : 
     637           0 :               break; // out of Newton iterations
     638             :             }
     639             : 
     640             :           //       ++attempt;
     641             :           //     } while ((attempt<max_attempts) && (rval.first==newton_solver->max_linear_iterations));
     642             : 
     643             : 
     644             : 
     645             : 
     646             : 
     647             :           // Compute N, the residual of the arclength constraint eqn.
     648             :           // Note 1: N(u,lambda,s) := (u-u_{old}, du_ds) + (lambda-lambda_{old}, dlambda_ds) - _ds
     649             :           // We temporarily use the delta_u vector as a temporary vector for this calculation.
     650           0 :           *delta_u = *solution;
     651           0 :           delta_u->add(-1., *previous_u);
     652             : 
     653             :           // First part of the arclength constraint
     654           0 :           const Number N1 = Theta_LOCA*Theta_LOCA*Theta*delta_u->dot(*du_ds);
     655           0 :           const Number N2 = ((*continuation_parameter) - old_continuation_parameter)*dlambda_ds;
     656           0 :           const Number N3 = ds_current;
     657             : 
     658           0 :           if (!quiet)
     659             :             {
     660           0 :               libMesh::out << "  N1=" << N1 << std::endl;
     661           0 :               libMesh::out << "  N2=" << N2 << std::endl;
     662           0 :               libMesh::out << "  N3=" << N3 << std::endl;
     663             :             }
     664             : 
     665             :           // The arclength constraint value
     666           0 :           const Number N = N1+N2-N3;
     667             : 
     668           0 :           if (!quiet)
     669           0 :             libMesh::out << "  N=" << N << std::endl;
     670             : 
     671           0 :           const Number duds_dot_z = du_ds->dot(*z);
     672           0 :           const Number duds_dot_y = du_ds->dot(*y);
     673             : 
     674             :           //libMesh::out << "duds_dot_z=" << duds_dot_z << std::endl;
     675             :           //libMesh::out << "duds_dot_y=" << duds_dot_y << std::endl;
     676             :           //libMesh::out << "dlambda_ds=" << dlambda_ds << std::endl;
     677             : 
     678           0 :           const Number delta_lambda_numerator   = -(N          + Theta_LOCA*Theta_LOCA*Theta*duds_dot_z);
     679           0 :           const Number delta_lambda_denominator =  (dlambda_ds - Theta_LOCA*Theta_LOCA*Theta*duds_dot_y);
     680             : 
     681           0 :           libmesh_assert_not_equal_to (delta_lambda_denominator, 0.0);
     682             : 
     683             :           // Now, we are ready to compute the step delta_lambda
     684           0 :           const Number delta_lambda_comp = delta_lambda_numerator /
     685             :             delta_lambda_denominator;
     686             :           // Lambda is real-valued
     687           0 :           const Real delta_lambda = libmesh_real(delta_lambda_comp);
     688             : 
     689             :           // Knowing delta_lambda, we are ready to update delta_u
     690             :           // delta_u = z - delta_lambda*y
     691           0 :           delta_u->zero();
     692           0 :           delta_u->add(1., *z);
     693           0 :           delta_u->add(-delta_lambda, *y);
     694           0 :           delta_u->close();
     695             : 
     696             :           // Update the system solution and the continuation parameter.
     697           0 :           solution->add(1., *delta_u);
     698           0 :           solution->close();
     699           0 :           *continuation_parameter += delta_lambda;
     700             : 
     701             :           // Did the Newton step actually reduce the residual?
     702           0 :           rhs_mode = Residual;
     703           0 :           assembly(true,   // Residual
     704           0 :                    false); // Jacobian
     705           0 :           rhs->close();
     706           0 :           nonlinear_residual_afterstep = rhs->l2_norm();
     707             : 
     708             : 
     709             :           // In a "normal" Newton step, ||du||/||R|| > 1 since the most recent
     710             :           // step is where you "just were" and the current residual is where
     711             :           // you are now.  It can occur that ||du||/||R|| < 1, but these are
     712             :           // likely not good cases to attempt backtracking (?).
     713           0 :           const Real norm_du_norm_R = delta_u->l2_norm() / nonlinear_residual_afterstep;
     714           0 :           if (!quiet)
     715           0 :             libMesh::out << "  norm_du_norm_R=" << norm_du_norm_R << std::endl;
     716             : 
     717             : 
     718             :           // Factor to decrease the stepsize by for backtracking
     719           0 :           Real newton_stepfactor = 1.;
     720             : 
     721           0 :           const bool attempt_backtracking =
     722           0 :             (nonlinear_residual_afterstep > solution_tolerance)
     723           0 :             && (nonlinear_residual_afterstep > nonlinear_residual_beforestep)
     724           0 :             && (n_backtrack_steps>0)
     725           0 :             && (norm_du_norm_R > 1.)
     726             :             ;
     727             : 
     728             :           // If residual is not reduced, do Newton back tracking.
     729           0 :           if (attempt_backtracking)
     730             :             {
     731           0 :               if (!quiet)
     732           0 :                 libMesh::out << "Newton step did not reduce residual." << std::endl;
     733             : 
     734             :               // back off the previous step.
     735           0 :               solution->add(-1., *delta_u);
     736           0 :               solution->close();
     737           0 :               *continuation_parameter -= delta_lambda;
     738             : 
     739             :               // Backtracking: start cutting the Newton stepsize by halves until
     740             :               // the new residual is actually smaller...
     741           0 :               for (unsigned int backtrack_step=0; backtrack_step<n_backtrack_steps; ++backtrack_step)
     742             :                 {
     743           0 :                   newton_stepfactor *= 0.5;
     744             : 
     745           0 :                   if (!quiet)
     746           0 :                     libMesh::out << "Shrinking step size by " << newton_stepfactor << std::endl;
     747             : 
     748             :                   // Take fractional step
     749           0 :                   solution->add(newton_stepfactor, *delta_u);
     750           0 :                   solution->close();
     751           0 :                   *continuation_parameter += newton_stepfactor*delta_lambda;
     752             : 
     753           0 :                   rhs_mode = Residual;
     754           0 :                   assembly(true,   // Residual
     755           0 :                            false); // Jacobian
     756           0 :                   rhs->close();
     757           0 :                   nonlinear_residual_afterstep = rhs->l2_norm();
     758             : 
     759           0 :                   if (!quiet)
     760           0 :                     libMesh::out << "At shrink step "
     761           0 :                                  << backtrack_step
     762           0 :                                  << ", nonlinear_residual_afterstep="
     763           0 :                                  << nonlinear_residual_afterstep
     764           0 :                                  << std::endl;
     765             : 
     766           0 :                   if (nonlinear_residual_afterstep < nonlinear_residual_beforestep)
     767             :                     {
     768           0 :                       if (!quiet)
     769           0 :                         libMesh::out << "Backtracking succeeded!" << std::endl;
     770             : 
     771           0 :                       break; // out of backtracking loop
     772             :                     }
     773             : 
     774             :                   else
     775             :                     {
     776             :                       // Back off that step
     777           0 :                       solution->add(-newton_stepfactor, *delta_u);
     778           0 :                       solution->close();
     779           0 :                       *continuation_parameter -= newton_stepfactor*delta_lambda;
     780             :                     }
     781             : 
     782             :                   // Save a copy of the solution from before the Newton step.
     783             :                   //std::unique_ptr<NumericVector<Number>> prior_iterate = solution->clone();
     784             :                 }
     785             :             } // end if (attempte_backtracking)
     786             : 
     787             : 
     788             :           // If we tried backtracking but the residual is still not reduced, print message.
     789           0 :           if ((attempt_backtracking) && (nonlinear_residual_afterstep > nonlinear_residual_beforestep))
     790             :             {
     791             :               //libMesh::err << "Backtracking failed." << std::endl;
     792           0 :               libMesh::out << "Backtracking failed." << std::endl;
     793             : 
     794             :               // 1.) Quit, exit program.
     795             :               //libmesh_error_msg("Backtracking failed!");
     796             : 
     797             :               // 2.) Continue with last newton_stepfactor
     798           0 :               if (newton_step<3)
     799             :                 {
     800           0 :                   solution->add(newton_stepfactor, *delta_u);
     801           0 :                   solution->close();
     802           0 :                   *continuation_parameter += newton_stepfactor*delta_lambda;
     803           0 :                   if (!quiet)
     804           0 :                     libMesh::out << "Backtracking could not reduce residual ... continuing anyway!" << std::endl;
     805             :                 }
     806             : 
     807             :               // 3.) Break out of Newton iteration loop with newton_converged = false,
     808             :               //     reduce the arclength stepsize, and try again.
     809             :               else
     810             :                 {
     811           0 :                   break; // out of Newton iteration loop, with newton_converged=false
     812             :                 }
     813             :             }
     814             : 
     815             :           // Another type of convergence check: suppose the residual has not been reduced
     816             :           // from its initial value after half of the allowed Newton steps have occurred.
     817             :           // In our experience, this typically means that it isn't going to converge and
     818             :           // we could probably save time by dropping out of the Newton iteration loop and
     819             :           // trying a smaller arcstep.
     820           0 :           if (this->newton_progress_check)
     821             :             {
     822           0 :               if ((nonlinear_residual_afterstep > nonlinear_residual_firststep) &&
     823           0 :                   (newton_step+1 > static_cast<unsigned int>(0.5*newton_solver->max_nonlinear_iterations)))
     824             :                 {
     825           0 :                   libMesh::out << "Progress check failed: the current residual: "
     826           0 :                                << nonlinear_residual_afterstep
     827           0 :                                << ", is\n"
     828           0 :                                << "larger than the initial residual, and half of the allowed\n"
     829           0 :                                << "number of Newton iterations have elapsed.\n"
     830           0 :                                << "Exiting Newton iterations with converged==false." << std::endl;
     831             : 
     832           0 :                   break; // out of Newton iteration loop, newton_converged = false
     833             :                 }
     834             :             }
     835             : 
     836             :           // Safety check: Check the current continuation parameter against user-provided min-allowable parameter value
     837           0 :           if (*continuation_parameter < min_continuation_parameter)
     838             :             {
     839           0 :               libMesh::out << "Continuation parameter fell below min-allowable value." << std::endl;
     840           0 :               break; // out of Newton iteration loop, newton_converged = false
     841             :             }
     842             : 
     843             :           // Safety check: Check the current continuation parameter against user-provided max-allowable parameter value
     844           0 :           if ( (max_continuation_parameter != 0.0) &&
     845           0 :                (*continuation_parameter > max_continuation_parameter) )
     846             :             {
     847           0 :               libMesh::out << "Current continuation parameter value: "
     848           0 :                            << *continuation_parameter
     849           0 :                            << " exceeded max-allowable value."
     850           0 :                            << std::endl;
     851           0 :               break; // out of Newton iteration loop, newton_converged = false
     852             :             }
     853             : 
     854             : 
     855             :           // Check the convergence of the parameter and the solution.  If they are small
     856             :           // enough, we can break out of the Newton iteration loop.
     857           0 :           const Real norm_delta_u = delta_u->l2_norm();
     858           0 :           const Real norm_u = solution->l2_norm();
     859           0 :           libMesh::out << "  delta_lambda                   = " << delta_lambda << std::endl;
     860           0 :           libMesh::out << "  newton_stepfactor*delta_lambda = " << newton_stepfactor*delta_lambda << std::endl;
     861           0 :           libMesh::out << "  lambda_current                 = " << *continuation_parameter << std::endl;
     862           0 :           libMesh::out << "  ||delta_u||                    = " << norm_delta_u << std::endl;
     863           0 :           libMesh::out << "  ||delta_u||/||u||              = " << norm_delta_u / norm_u << std::endl;
     864             : 
     865             : 
     866             :           // Evaluate the residual at the current Newton iterate.  We don't want to detect
     867             :           // convergence due to a small Newton step when the residual is still not small.
     868           0 :           rhs_mode = Residual;
     869           0 :           assembly(true,   // Residual
     870           0 :                    false); // Jacobian
     871           0 :           rhs->close();
     872           0 :           const Real norm_residual = rhs->l2_norm();
     873           0 :           libMesh::out << "  ||R||_{L2} = " << norm_residual << std::endl;
     874           0 :           libMesh::out << "  ||R||_{L2}/||u|| = " << norm_residual / norm_u << std::endl;
     875             : 
     876             : 
     877             :           // FIXME: The norm_delta_u tolerance (at least) should be relative.
     878             :           // It doesn't make sense to converge a solution whose size is ~ 10^5 to
     879             :           // a tolerance of 1.e-6.  Oh, and we should also probably check the
     880             :           // (relative) size of the residual as well, instead of just the step.
     881           0 :           if ((std::abs(delta_lambda) < continuation_parameter_tolerance) &&
     882             :               //(norm_delta_u       < solution_tolerance)               && // This is a *very* strict criterion we can probably skip
     883           0 :               (norm_residual      < solution_tolerance))
     884             :             {
     885           0 :               if (!quiet)
     886           0 :                 libMesh::out << "Newton iterations converged!" << std::endl;
     887             : 
     888           0 :               newton_converged = true;
     889           0 :               break; // out of Newton iterations
     890             :             }
     891             :         } // end nonlinear loop
     892             : 
     893           0 :       if (!newton_converged)
     894             :         {
     895           0 :           libMesh::out << "Newton iterations of augmented system did not converge!" << std::endl;
     896             : 
     897             :           // Reduce ds_current, recompute the solution and parameter, and continue to next
     898             :           // arcstep, if there is one.
     899           0 :           ds_current *= 0.5;
     900             : 
     901             :           // Go back to previous solution and parameter value.
     902           0 :           *solution = *previous_u;
     903           0 :           *continuation_parameter = old_continuation_parameter;
     904             : 
     905             :           // Compute new predictor with smaller ds
     906           0 :           apply_predictor();
     907             :         }
     908             :       else
     909             :         {
     910             :           // Set step convergence and break out
     911           0 :           arcstep_converged=true;
     912           0 :           break; // out of arclength reduction loop
     913             :         }
     914             : 
     915             :     } // end loop over arclength reductions
     916             : 
     917             :   // Check for convergence of the whole arcstep.  If not converged at this
     918             :   // point, we have no choice but to quit.
     919           0 :   libmesh_error_msg_if(!arcstep_converged, "Arcstep failed to converge after max number of reductions! Exiting...");
     920             : 
     921             :   // Print converged solution control parameter and max value.
     922           0 :   libMesh::out << "lambda_current=" << *continuation_parameter << std::endl;
     923             :   //libMesh::out << "u_max=" << solution->max() << std::endl;
     924             : 
     925             :   // Reset old stream precision and flags.
     926           0 :   libMesh::out.precision(old_precision);
     927           0 :   libMesh::out.unsetf(std::ios_base::scientific);
     928             : 
     929             :   // Note: we don't want to go on to the next guess yet, since the user may
     930             :   // want to post-process this data.  It's up to the user to call advance_arcstep()
     931             :   // when they are ready to go on.
     932           0 : }
     933             : 
     934             : 
     935             : 
     936           0 : void ContinuationSystem::advance_arcstep()
     937             : {
     938             :   // Solve for the updated tangent du1/ds, d(lambda1)/ds
     939           0 :   solve_tangent();
     940             : 
     941             :   // Advance the solution and the parameter to the next value.
     942           0 :   update_solution();
     943           0 : }
     944             : 
     945             : 
     946             : 
     947             : // This function solves the tangent system:
     948             : // [ G_u                G_{lambda}        ][(du/ds)_new      ] = [  0 ]
     949             : // [ Theta*(du/ds)_old  (dlambda/ds)_old  ][(dlambda/ds)_new ]   [-N_s]
     950             : // The solution is given by:
     951             : // .) Let G_u y = G_lambda, then
     952             : // .) 2nd row yields:
     953             : //    (dlambda/ds)_new = 1.0 / ( (dlambda/ds)_old - Theta*(du/ds)_old*y )
     954             : // .) 1st row yields
     955             : //    (du_ds)_new = -(dlambda/ds)_new * y
     956           0 : void ContinuationSystem::solve_tangent()
     957             : {
     958             :   // We shouldn't call this unless the current tangent already makes sense.
     959           0 :   libmesh_assert (tangent_initialized);
     960             : 
     961             :   // Set pointer to underlying Newton solver
     962           0 :   if (!newton_solver)
     963           0 :     newton_solver =
     964           0 :       cast_ptr<NewtonSolver *> (this->time_solver->diff_solver().get());
     965             : 
     966             :   // Assemble the system matrix AND rhs, with rhs = G_{\lambda}
     967           0 :   this->rhs_mode = G_Lambda;
     968             : 
     969             :   // Assemble Residual and Jacobian
     970           0 :   this->assembly(true,   // Residual
     971           0 :                  true); // Jacobian
     972             : 
     973             :   // Not sure if this is really necessary
     974           0 :   rhs->close();
     975             : 
     976             :   // Solve G_u*y =  G_{\lambda}
     977             :   std::pair<unsigned int, Real> rval =
     978           0 :     linear_solver->solve(*matrix,
     979           0 :                          *y,
     980           0 :                          *rhs,
     981           0 :                          1.e-12, // relative linear tolerance
     982           0 :                          2*newton_solver->max_linear_iterations);   // max linear iterations
     983             : 
     984             :   // FIXME: If this doesn't converge at all, the new tangent vector is
     985             :   // going to be really bad...
     986             : 
     987           0 :   if (!quiet)
     988           0 :     libMesh::out << "G_u*y = G_{lambda} solver converged at step "
     989           0 :                  << rval.first
     990           0 :                  << " linear tolerance = "
     991           0 :                  << rval.second
     992           0 :                  << "."
     993           0 :                  << std::endl;
     994             : 
     995             :   // Save old solution and parameter tangents for possible use in higher-order
     996             :   // predictor schemes.
     997           0 :   previous_dlambda_ds = dlambda_ds;
     998           0 :   *previous_du_ds     = *du_ds;
     999             : 
    1000             : 
    1001             :   // 1.) Previous, probably wrong, technique!
    1002             :   //   // Solve for the updated d(lambda)/ds
    1003             :   //   // denom = N_{lambda}   - (du_ds)^t y
    1004             :   //   //       = d(lambda)/ds - (du_ds)^t y
    1005             :   //   Real denom = dlambda_ds - du_ds->dot(*y);
    1006             : 
    1007             :   //   //libMesh::out << "denom=" << denom << std::endl;
    1008             :   //   libmesh_assert_not_equal_to (denom, 0.0);
    1009             : 
    1010             :   //   dlambda_ds = 1.0 / denom;
    1011             : 
    1012             : 
    1013             :   //   if (!quiet)
    1014             :   //     libMesh::out << "dlambda_ds=" << dlambda_ds << std::endl;
    1015             : 
    1016             :   //   // Compute the updated value of du/ds = -_dlambda_ds * y
    1017             :   //   du_ds->zero();
    1018             :   //   du_ds->add(-dlambda_ds, *y);
    1019             :   //   du_ds->close();
    1020             : 
    1021             : 
    1022             :   // 2.) From Brian Carnes' paper...
    1023             :   // According to Carnes, y comes from solving G_u * y = -G_{\lambda}
    1024           0 :   y->scale(-1.);
    1025           0 :   const Real ynorm = y->l2_norm();
    1026           0 :   dlambda_ds = 1. / std::sqrt(1. + Theta_LOCA*Theta_LOCA*Theta*ynorm*ynorm);
    1027             : 
    1028             :   // Determine the correct sign for dlambda_ds.
    1029             : 
    1030             :   // We will use delta_u to temporarily compute this sign.
    1031           0 :   *delta_u = *solution;
    1032           0 :   delta_u->add(-1., *previous_u);
    1033           0 :   delta_u->close();
    1034             : 
    1035             :   const Real sgn_dlambda_ds =
    1036           0 :     libmesh_real(Theta_LOCA*Theta_LOCA*Theta*y->dot(*delta_u) +
    1037           0 :                  (*continuation_parameter-old_continuation_parameter));
    1038             : 
    1039           0 :   if (sgn_dlambda_ds < 0.)
    1040             :     {
    1041           0 :       if (!quiet)
    1042           0 :         libMesh::out << "dlambda_ds is negative." << std::endl;
    1043             : 
    1044           0 :       dlambda_ds *= -1.;
    1045             :     }
    1046             : 
    1047             :   // Finally, set the new tangent vector, du/ds = dlambda/ds * y.
    1048           0 :   du_ds->zero();
    1049           0 :   du_ds->add(dlambda_ds, *y);
    1050           0 :   du_ds->close();
    1051             : 
    1052           0 :   if (!quiet)
    1053             :     {
    1054           0 :       libMesh::out << "d(lambda)/ds = " << dlambda_ds << std::endl;
    1055           0 :       libMesh::out << "||du_ds||    = " << du_ds->l2_norm() << std::endl;
    1056             :     }
    1057             : 
    1058             :   // Our next solve expects y ~ -du/dlambda, so scale it back by -1 again now.
    1059           0 :   y->scale(-1.);
    1060           0 :   y->close();
    1061           0 : }
    1062             : 
    1063             : 
    1064             : 
    1065           0 : void ContinuationSystem::set_Theta()
    1066             : {
    1067             :   // // Use the norm of the latest solution, squared.
    1068             :   //const Real normu = solution->l2_norm();
    1069             :   //libmesh_assert_not_equal_to (normu, 0.0);
    1070             :   //Theta = 1./normu/normu;
    1071             : 
    1072             :   // // 1.) Use the norm of du, squared
    1073             :   //   *delta_u = *solution;
    1074             :   //   delta_u->add(-1, *previous_u);
    1075             :   //   delta_u->close();
    1076             :   //   const Real normdu = delta_u->l2_norm();
    1077             : 
    1078             :   //   if (normdu < 1.) // don't divide by zero or make a huge scaling parameter.
    1079             :   //     Theta = 1.;
    1080             :   //   else
    1081             :   //     Theta = 1./normdu/normdu;
    1082             : 
    1083             :   // 2.) Use 1.0, i.e. don't scale
    1084           0 :   Theta=1.;
    1085             : 
    1086             :   // 3.) Use a formula which attempts to make the "solution triangle" isosceles.
    1087             :   //   libmesh_assert_less (std::abs(dlambda_ds), 1.);
    1088             : 
    1089             :   //   *delta_u = *solution;
    1090             :   //   delta_u->add(-1, *previous_u);
    1091             :   //   delta_u->close();
    1092             :   //   const Real normdu = delta_u->l2_norm();
    1093             : 
    1094             :   //   Theta = std::sqrt(1. - dlambda_ds*dlambda_ds) / normdu * tau * ds;
    1095             : 
    1096             : 
    1097             :   //   // 4.) Use the norm of du and the norm of du/ds
    1098             :   //   *delta_u = *solution;
    1099             :   //   delta_u->add(-1, *previous_u);
    1100             :   //   delta_u->close();
    1101             :   //   const Real normdu   = delta_u->l2_norm();
    1102             :   //   du_ds->close();
    1103             :   //   const Real normduds = du_ds->l2_norm();
    1104             : 
    1105             :   //   if (normduds < 1.e-12)
    1106             :   //     {
    1107             :   //       libMesh::out << "Setting initial Theta= 1./normdu/normdu" << std::endl;
    1108             :   //       libMesh::out << "normdu=" << normdu << std::endl;
    1109             : 
    1110             :   //       // Don't use this scaling if the solution delta is already O(1)
    1111             :   //       if (normdu > 1.)
    1112             :   // Theta = 1./normdu/normdu;
    1113             :   //       else
    1114             :   // Theta = 1.;
    1115             :   //     }
    1116             :   //   else
    1117             :   //     {
    1118             :   //       libMesh::out << "Setting Theta= 1./normdu/normduds" << std::endl;
    1119             :   //       libMesh::out << "normdu=" << normdu << std::endl;
    1120             :   //       libMesh::out << "normduds=" << normduds << std::endl;
    1121             : 
    1122             :   //       // Don't use this scaling if the solution delta is already O(1)
    1123             :   //       if ((normdu>1.) || (normduds>1.))
    1124             :   // Theta = 1./normdu/normduds;
    1125             :   //       else
    1126             :   // Theta = 1.;
    1127             :   //     }
    1128             : 
    1129           0 :   if (!quiet)
    1130           0 :     libMesh::out << "Setting Normalization Parameter Theta=" << Theta << std::endl;
    1131           0 : }
    1132             : 
    1133             : 
    1134             : 
    1135           0 : void ContinuationSystem::set_Theta_LOCA()
    1136             : {
    1137             :   // We also recompute the LOCA normalization parameter based on the
    1138             :   // most recently computed value of dlambda_ds
    1139             :   // if (!quiet)
    1140             :   //   libMesh::out << "(Theta_LOCA) dlambda_ds=" << dlambda_ds << std::endl;
    1141             : 
    1142             :   // Formula makes no sense if |dlambda_ds| > 1
    1143           0 :   libmesh_assert_less (std::abs(dlambda_ds), 1.);
    1144             : 
    1145             :   // 1.) Attempt to implement the method in LOCA paper
    1146             :   //   const Real g = 1./std::sqrt(2.); // "desired" dlambda_ds
    1147             : 
    1148             :   //   // According to the LOCA people, we only renormalize for
    1149             :   //   // when |dlambda_ds| exceeds some pre-selected maximum (which they take to be zero, btw).
    1150             :   //   if (std::abs(dlambda_ds) > .9)
    1151             :   //     {
    1152             :   //       // Note the *= ... This is updating the previous value of Theta_LOCA
    1153             :   //       // Note: The LOCA people actually use Theta_LOCA^2 to normalize their arclength constraint.
    1154             :   //       Theta_LOCA *= std::abs( (dlambda_ds/g)*std::sqrt( (1.-g*g) / (1.-dlambda_ds*dlambda_ds) ) );
    1155             : 
    1156             :   //       // Suggested max-allowable value for Theta_LOCA
    1157             :   //       if (Theta_LOCA > 1.e8)
    1158             :   // {
    1159             :   //   Theta_LOCA = 1.e8;
    1160             : 
    1161             :   //   if (!quiet)
    1162             :   //     libMesh::out << "max Theta_LOCA=" << Theta_LOCA << " has been selected." << std::endl;
    1163             :   // }
    1164             :   //     }
    1165             :   //   else
    1166             :   //     Theta_LOCA=1.0;
    1167             : 
    1168             :   // 2.) FIXME: Should we do *= or just =?  This function is of dlambda_ds is
    1169             :   //  < 1,  |dlambda_ds| < 1/sqrt(2) ~~ .7071
    1170             :   //  > 1,  |dlambda_ds| > 1/sqrt(2) ~~ .7071
    1171           0 :   Theta_LOCA *= std::abs( dlambda_ds / std::sqrt( (1.-dlambda_ds*dlambda_ds) ) );
    1172             : 
    1173             :   // Suggested max-allowable value for Theta_LOCA.  I've never come close
    1174             :   // to this value in my code.
    1175           0 :   if (Theta_LOCA > 1.e8)
    1176             :     {
    1177           0 :       Theta_LOCA = 1.e8;
    1178             : 
    1179           0 :       if (!quiet)
    1180           0 :         libMesh::out << "max Theta_LOCA=" << Theta_LOCA << " has been selected." << std::endl;
    1181             :     }
    1182             : 
    1183             :   // 3.) Use 1.0, i.e. don't scale
    1184             :   //Theta_LOCA=1.0;
    1185             : 
    1186           0 :   if (!quiet)
    1187           0 :     libMesh::out << "Setting Theta_LOCA=" << Theta_LOCA << std::endl;
    1188           0 : }
    1189             : 
    1190             : 
    1191             : 
    1192           0 : void ContinuationSystem::update_solution()
    1193             : {
    1194             :   // Set some stream formatting flags
    1195           0 :   std::streamsize old_precision = libMesh::out.precision();
    1196           0 :   libMesh::out.precision(16);
    1197           0 :   libMesh::out.setf(std::ios_base::scientific);
    1198             : 
    1199             :   // We must have a tangent that makes sense before we can update the solution.
    1200           0 :   libmesh_assert (tangent_initialized);
    1201             : 
    1202             :   // Compute tau, the stepsize scaling parameter which attempts to
    1203             :   // reduce ds when the angle between the most recent two tangent
    1204             :   // vectors becomes large.  tau is actually the (absolute value of
    1205             :   // the) cosine of the angle between these two vectors... so if tau ~
    1206             :   // 0 the angle is ~ 90 degrees, while if tau ~ 1 the angle is ~ 0
    1207             :   // degrees.
    1208           0 :   y_old->close();
    1209           0 :   y->close();
    1210           0 :   const Real yoldnorm = y_old->l2_norm();
    1211           0 :   const Real ynorm = y->l2_norm();
    1212           0 :   const Number yoldy = y_old->dot(*y);
    1213           0 :   const Real yold_over_y = yoldnorm/ynorm;
    1214             : 
    1215           0 :   if (!quiet)
    1216             :     {
    1217           0 :       libMesh::out << "yoldnorm=" << yoldnorm << std::endl;
    1218           0 :       libMesh::out << "ynorm="    << ynorm << std::endl;
    1219           0 :       libMesh::out << "yoldy="    << yoldy << std::endl;
    1220           0 :       libMesh::out << "yoldnorm/ynorm=" << yoldnorm/ynorm << std::endl;
    1221             :     }
    1222             : 
    1223             :   // Save the current value of ds before updating it
    1224           0 :   previous_ds = ds_current;
    1225             : 
    1226             :   // // 1.) Cosine method (for some reason this always predicts the angle is ~0)
    1227             :   // // Don't try dividing by zero
    1228             :   // if ((yoldnorm > 1.e-12) && (ynorm > 1.e-12))
    1229             :   //   tau = std::abs(yoldy) / yoldnorm  / ynorm;
    1230             :   // else
    1231             :   //   tau = 1.;
    1232             : 
    1233             :   // // 2.) Relative size of old and new du/dlambda method with cutoff of 0.9
    1234             :   // if ((yold_over_y < 0.9) && (yold_over_y > 1.e-6))
    1235             :   //   tau = yold_over_y;
    1236             :   // else
    1237             :   //   tau = 1.;
    1238             : 
    1239             :   // 3.) Grow (or shrink) the arclength stepsize by the ratio of du/dlambda, but do not
    1240             :   // exceed the user-specified value of ds.
    1241           0 :   if (yold_over_y > 1.e-6)
    1242             :     {
    1243             :       // // 1.) Scale current ds by the ratio of successive tangents.
    1244             :       //       ds_current *= yold_over_y;
    1245             :       //       if (ds_current > ds)
    1246             :       // ds_current = ds;
    1247             : 
    1248             :       // 2.) Technique 1 tends to shrink the step fairly well (and even if it doesn't
    1249             :       // get very small, we still have step reduction) but it seems to grow the step
    1250             :       // very slowly.  Another possible technique is step-doubling:
    1251             :       //       if (yold_over_y > 1.)
    1252             :       //       ds_current *= 2.;
    1253             :       //       else
    1254             :       // ds_current *= yold_over_y;
    1255             : 
    1256             :       // 3.) Technique 2 may over-zealous when we are also using the Newton stepgrowth
    1257             :       // factor.  For technique 3 we multiply by yold_over_y unless yold_over_y > 2
    1258             :       // in which case we use 2.
    1259             :       //       if (yold_over_y > 2.)
    1260             :       // ds_current *= 2.;
    1261             :       //       else
    1262             :       // ds_current *= yold_over_y;
    1263             : 
    1264             :       // 4.) Double-or-halve.  We double the arc-step if the ratio of successive tangents
    1265             :       // is larger than 'double_threshold', halve it if it is less than 'halve_threshold'
    1266           0 :       const Real double_threshold = 0.5;
    1267           0 :       const Real halve_threshold  = 0.5;
    1268           0 :       if (yold_over_y > double_threshold)
    1269           0 :         ds_current *= 2.;
    1270           0 :       else if (yold_over_y < halve_threshold)
    1271           0 :         ds_current *= 0.5;
    1272             : 
    1273             : 
    1274             :       // Also possibly use the number of Newton iterations required to compute the previous
    1275             :       // step (relative to the maximum-allowed number of Newton iterations) to grow the step.
    1276           0 :       if (newton_stepgrowth_aggressiveness > 0.)
    1277             :         {
    1278           0 :           libmesh_assert(newton_solver);
    1279           0 :           const unsigned int Nmax = newton_solver->max_nonlinear_iterations;
    1280             : 
    1281             :           // // The LOCA Newton step growth technique (note: only grows step length)
    1282             :           // const Real stepratio = static_cast<Real>(Nmax-(newton_step+1))/static_cast<Real>(Nmax-1.);
    1283             :           // const Real newtonstep_growthfactor = 1. + newton_stepgrowth_aggressiveness*stepratio*stepratio;
    1284             : 
    1285             :           // The "Nopt/N" method, may grow or shrink the step.  Assume Nopt=Nmax/2.
    1286           0 :           const Real newtonstep_growthfactor =
    1287           0 :             newton_stepgrowth_aggressiveness * 0.5 *
    1288           0 :             static_cast<Real>(Nmax) / static_cast<Real>(newton_step+1);
    1289             : 
    1290           0 :           if (!quiet)
    1291           0 :             libMesh::out << "newtonstep_growthfactor=" << newtonstep_growthfactor << std::endl;
    1292             : 
    1293           0 :           ds_current *= newtonstep_growthfactor;
    1294             :         }
    1295             :     }
    1296             : 
    1297             : 
    1298             :   // Don't let the stepsize get above the user's maximum-allowed stepsize.
    1299           0 :   if (ds_current > ds)
    1300           0 :     ds_current = ds;
    1301             : 
    1302             :   // Check also for a minimum allowed stepsize.
    1303           0 :   if (ds_current < ds_min)
    1304             :     {
    1305           0 :       libMesh::out << "Enforcing minimum-allowed arclength stepsize of " << ds_min << std::endl;
    1306           0 :       ds_current = ds_min;
    1307             :     }
    1308             : 
    1309           0 :   if (!quiet)
    1310             :     {
    1311           0 :       libMesh::out << "Current step size: ds_current=" << ds_current << std::endl;
    1312             :     }
    1313             : 
    1314             :   // Recompute scaling factor Theta for
    1315             :   // the current solution before updating.
    1316           0 :   set_Theta();
    1317             : 
    1318             :   // Also, recompute the LOCA scaling factor, which attempts to
    1319             :   // maintain a reasonable value of dlambda/ds
    1320           0 :   set_Theta_LOCA();
    1321             : 
    1322           0 :   libMesh::out << "Theta*Theta_LOCA^2=" << Theta*Theta_LOCA*Theta_LOCA << std::endl;
    1323             : 
    1324             :   // Based on the asymptotic singular behavior of du/dlambda near simple turning points,
    1325             :   // we can compute a single parameter which may suggest that we are close to a singularity.
    1326           0 :   *delta_u = *solution;
    1327           0 :   delta_u->add(-1, *previous_u);
    1328           0 :   delta_u->close();
    1329           0 :   const Real normdu   = delta_u->l2_norm();
    1330           0 :   const Real C = (std::log (Theta_LOCA*normdu) /
    1331           0 :                   std::log (std::abs(*continuation_parameter-old_continuation_parameter))) - 1.0;
    1332           0 :   if (!quiet)
    1333           0 :     libMesh::out << "C=" << C << std::endl;
    1334             : 
    1335             :   // Save the current value of u and lambda before updating.
    1336           0 :   save_current_solution();
    1337             : 
    1338           0 :   if (!quiet)
    1339             :     {
    1340           0 :       libMesh::out << "Updating the solution with the tangent guess." << std::endl;
    1341           0 :       libMesh::out << "||u_old||=" << this->solution->l2_norm() << std::endl;
    1342           0 :       libMesh::out << "lambda_old=" << *continuation_parameter << std::endl;
    1343             :     }
    1344             : 
    1345             :   // Since we solved for the tangent vector, now we can compute an
    1346             :   // initial guess for the new solution, and an initial guess for the
    1347             :   // new value of lambda.
    1348           0 :   apply_predictor();
    1349             : 
    1350           0 :   if (!quiet)
    1351             :     {
    1352           0 :       libMesh::out << "||u_new||=" << this->solution->l2_norm() << std::endl;
    1353           0 :       libMesh::out << "lambda_new=" << *continuation_parameter << std::endl;
    1354             :     }
    1355             : 
    1356             :   // Unset previous stream flags
    1357           0 :   libMesh::out.precision(old_precision);
    1358           0 :   libMesh::out.unsetf(std::ios_base::scientific);
    1359           0 : }
    1360             : 
    1361             : 
    1362             : 
    1363             : 
    1364           0 : void ContinuationSystem::save_current_solution()
    1365             : {
    1366             :   // Save the old solution vector
    1367           0 :   *previous_u = *solution;
    1368             : 
    1369             :   // Save the old value of lambda
    1370           0 :   old_continuation_parameter = *continuation_parameter;
    1371           0 : }
    1372             : 
    1373             : 
    1374             : 
    1375           0 : void ContinuationSystem::apply_predictor()
    1376             : {
    1377           0 :   if (predictor == Euler)
    1378             :     {
    1379             :       // 1.) Euler Predictor
    1380             :       // Predict next the solution
    1381           0 :       solution->add(ds_current, *du_ds);
    1382           0 :       solution->close();
    1383             : 
    1384             :       // Predict next parameter value
    1385           0 :       *continuation_parameter += ds_current*dlambda_ds;
    1386             :     }
    1387             : 
    1388             : 
    1389           0 :   else if (predictor == AB2)
    1390             :     {
    1391             :       // 2.) 2nd-order explicit AB predictor
    1392           0 :       libmesh_assert_not_equal_to (previous_ds, 0.0);
    1393           0 :       const Real stepratio = ds_current/previous_ds;
    1394             : 
    1395             :       // Build up next solution value.
    1396           0 :       solution->add( 0.5*ds_current*(2.+stepratio), *du_ds);
    1397           0 :       solution->add(-0.5*ds_current*stepratio     , *previous_du_ds);
    1398           0 :       solution->close();
    1399             : 
    1400             :       // Next parameter value
    1401           0 :       *continuation_parameter +=
    1402           0 :         0.5*ds_current*((2.+stepratio)*dlambda_ds -
    1403           0 :                         stepratio*previous_dlambda_ds);
    1404             :     }
    1405             : 
    1406             :   else
    1407           0 :     libmesh_error_msg("Unknown predictor!");
    1408           0 : }
    1409             : 
    1410             : } // namespace libMesh

Generated by: LCOV version 1.14