libMesh
Loading...
Searching...
No Matches
continuation_system.C
Go to the documentation of this file.
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// 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
27namespace libMesh
28{
29
31 const std::string & name_in,
32 const unsigned int number_in) :
33 Parent(es, name_in, number_in),
34 continuation_parameter(nullptr),
35 quiet(true),
36 continuation_parameter_tolerance(1.e-6),
37 solution_tolerance(1.e-6),
38 initial_newton_tolerance(0.01),
39 old_continuation_parameter(0.),
40 min_continuation_parameter(0.),
41 max_continuation_parameter(0.),
42 Theta(1.),
43 Theta_LOCA(1.),
44 n_backtrack_steps(5),
45 n_arclength_reductions(5),
46 ds_min(1.e-8),
47 predictor(Euler),
48 newton_stepgrowth_aggressiveness(1.),
49 newton_progress_check(true),
50 rhs_mode(Residual),
51 tangent_initialized(false),
52 newton_solver(nullptr),
53 dlambda_ds(0.707),
54 ds(0.1),
55 ds_current(0.1),
56 previous_dlambda_ds(0.),
57 previous_ds(0.),
58 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.
67
68 if (this->prefix_with_name())
69 linear_solver->init(this->prefix().c_str());
70 else
71 linear_solver->init();
72
73 linear_solver->init_systems(*this);
74}
75
76
77
78
80
81
82
84{
85 // Call the Parent's clear function
87}
88
89
90
92{
93 // Add a vector which stores the tangent "du/ds" to the system and save its pointer.
94 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 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 previous_u = &(add_vector("previous_u"));
102
103 // Add a vector to keep track of the temporary solution "y" of Ay=G_{\lambda}.
104 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 y_old = &(add_vector("y_old"));
108
109 // Add a vector to keep track of the temporary solution "z" of Az=-G.
110 z = &(add_vector("z"));
111
112 // Add a vector to keep track of the Newton update during the constrained PDE solves.
113 delta_u = &(add_vector("delta_u"));
114
115 // Call the Parent's initialization routine.
117}
118
119
120
121
123{
124 // Set the Residual RHS mode, and call the normal solve routine.
127}
128
129
130
131
133{
134 // Be sure the tangent was not already 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
292 libmesh_assert_not_equal_to (dlambda, 0.0);
293
294 // Ideal initial value of dlambda_ds
295 dlambda_ds = 1. / std::sqrt(2.);
296 if (dlambda < 0.)
297 dlambda_ds *= -1.;
298
299 // This also implies the initial value of ds
300 ds_current = dlambda / dlambda_ds;
301
302 if (!quiet)
303 libMesh::out << "Setting ds_current|_0=" << ds_current << std::endl;
304
305 // Set y = -du/dlambda using finite difference approximation
306 *y = *solution;
307 y->add(-1., *previous_u);
308 y->scale(-1./dlambda);
309 y->close();
310 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 *du_ds = *y;
316 du_ds->close();
317
318 // Determine additional solution normalization parameter
319 // (Since we just set du/ds, it will be: ||du||*||du/ds||)
320 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 Theta_LOCA = std::sqrt(1./Theta/ynorm/ynorm);
328
329
330 if (!quiet)
331 {
332 libMesh::out << "Setting initial Theta_LOCA = " << Theta_LOCA << std::endl;
333 libMesh::out << "Theta_LOCA^2*Theta = " << Theta_LOCA*Theta_LOCA*Theta << std::endl;
334 libMesh::out << "initial d(lambda)/ds|_0 = " << dlambda_ds << std::endl;
335 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 tangent_initialized = true;
345
347
348 // Advance the solution and the parameter to the next value.
350}
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.
360{
361 // Be sure the user has set the continuation parameter pointer
362 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 std::streamsize old_precision = libMesh::out.precision();
371 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.
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 *y_old = *y;
386
387 // Set pointer to underlying Newton solver
388 if (!newton_solver)
389 newton_solver = cast_ptr<NewtonSolver *> (this->time_solver->diff_solver().get());
390
391 // A pair for catching return values from linear system solves.
392 std::pair<unsigned int, Real> rval;
393
394 // Convergence flag for the entire arcstep
395 bool arcstep_converged = false;
396
397 // Begin loop over arcstep reductions.
398 for (unsigned int ns=0; ns<n_arclength_reductions; ++ns)
399 {
400 if (!quiet)
401 {
402 libMesh::out << "Current arclength stepsize, ds_current=" << ds_current << std::endl;
403 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 bool newton_converged = false;
409
410 // The nonlinear residual before *any* nonlinear steps have been taken.
411 Real nonlinear_residual_firststep = 0.;
412
413 // The nonlinear residual from the current "k" Newton step, before the Newton step
414 Real nonlinear_residual_beforestep = 0.;
415
416 // The nonlinear residual from the current "k" Newton step, after the Newton step
417 Real nonlinear_residual_afterstep = 0.;
418
419 // The linear solver tolerance, can be updated dynamically at each Newton step.
420 double current_linear_tolerance = 0.;
421
422 // The nonlinear loop
424 {
425 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 if (newton_step==0)
438 {
439 // At first step, only try reducing the residual by a small amount
440 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 const Real alp=0.5*(1.+std::sqrt(5.));
447 const Real gam=0.9;
448
449 libmesh_assert_not_equal_to (nonlinear_residual_beforestep, 0.0);
450 libmesh_assert_not_equal_to (nonlinear_residual_afterstep, 0.0);
451
452 current_linear_tolerance =
453 double(std::min(gam*std::pow(nonlinear_residual_afterstep/nonlinear_residual_beforestep, alp),
454 current_linear_tolerance*current_linear_tolerance));
455
456 // Don't let it get ridiculously small!!
457 if (current_linear_tolerance < 1.e-12)
458 current_linear_tolerance = 1.e-12;
459 }
460
461 if (!quiet)
462 libMesh::out << "Using current_linear_tolerance=" << current_linear_tolerance << std::endl;
463
464
465 // Assemble the residual (and Jacobian).
467 assembly(true, // Residual
468 true); // Jacobian
469 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 if (newton_step==0)
475 {
476 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 nonlinear_residual_firststep = nonlinear_residual_beforestep;
482
483 const Real old_norm_u = solution->l2_norm();
484 libMesh::out << " (before step) ||R||_{L2} = " << nonlinear_residual_beforestep << std::endl;
485 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 if (nonlinear_residual_beforestep < solution_tolerance)
490 {
491 if (!quiet)
492 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 matrix->close();
497 newton_converged=true;
498 break; // out of Newton iterations, with newton_converged=true
499 }
500 }
501
502 else
503 {
504 nonlinear_residual_beforestep = nonlinear_residual_afterstep;
505 }
506
507
508 // Solve the linear system G_u*z = G
509 // Initial guess?
510 z->zero(); // It seems to be extremely important to zero z here, otherwise the solver quits early.
511 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 do
518 {
519 rval =
520 linear_solver->solve(*matrix,
521 *z,
522 *rhs,
523 //1.e-12,
524 current_linear_tolerance,
525 newton_solver->max_linear_iterations); // max linear iterations
526
527 if (rval.first==0)
528 {
529 if (newton_step==0)
530 {
531 libMesh::out << "Repeating initial solve with smaller linear tolerance!" << std::endl;
532 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 libmesh_error_msg("Linear solver did no work!");
539 }
540
541 } while (rval.first==0);
542
543
544 if (!quiet)
545 libMesh::out << " G_u*z = G solver converged at step "
546 << rval.first
547 << " linear tolerance = "
548 << rval.second
549 << "."
550 << 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 if ((rval.first == 0) && (rval.second > current_linear_tolerance*nonlinear_residual_beforestep))
558 {
559 if (!quiet)
560 libMesh::out << "Linear solver exited in zero iterations!" << std::endl;
561
562 // Try to find out the reason for convergence/divergence
563 linear_solver->print_converged_reason();
564
565 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 z->scale(-1.);
571 z->close();
572
573
574
575
576
577
578 // Assemble the G_Lambda vector, skip residual.
580
581 // Assemble both rhs and Jacobian
582 assembly(true, // Residual
583 false); // Jacobian
584
585 // Not sure if this is really necessary
586 rhs->close();
587 const Real yrhsnorm=rhs->l2_norm();
588 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 const double ysystemtol =
593 double(current_linear_tolerance*(nonlinear_residual_beforestep/yrhsnorm));
594 if (!quiet)
595 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 linear_solver->solve(*matrix,
614 *y,
615 *rhs,
616 //1.e-12,
617 ysystemtol,
618 newton_solver->max_linear_iterations); // max linear iterations
619
620 if (!quiet)
621 libMesh::out << " G_u*y = G_{lambda} solver converged at step "
622 << rval.first
623 << ", linear tolerance = "
624 << rval.second
625 << "."
626 << 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 if ((rval.first == 0) && (rval.second > ysystemtol))
633 {
634 if (!quiet)
635 libMesh::out << "Linear solver exited in zero iterations!" << std::endl;
636
637 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 *delta_u = *solution;
651 delta_u->add(-1., *previous_u);
652
653 // First part of the arclength constraint
655 const Number N2 = ((*continuation_parameter) - old_continuation_parameter)*dlambda_ds;
656 const Number N3 = ds_current;
657
658 if (!quiet)
659 {
660 libMesh::out << " N1=" << N1 << std::endl;
661 libMesh::out << " N2=" << N2 << std::endl;
662 libMesh::out << " N3=" << N3 << std::endl;
663 }
664
665 // The arclength constraint value
666 const Number N = N1+N2-N3;
667
668 if (!quiet)
669 libMesh::out << " N=" << N << std::endl;
670
671 const Number duds_dot_z = du_ds->dot(*z);
672 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 const Number delta_lambda_numerator = -(N + Theta_LOCA*Theta_LOCA*Theta*duds_dot_z);
679 const Number delta_lambda_denominator = (dlambda_ds - Theta_LOCA*Theta_LOCA*Theta*duds_dot_y);
680
681 libmesh_assert_not_equal_to (delta_lambda_denominator, 0.0);
682
683 // Now, we are ready to compute the step delta_lambda
684 const Number delta_lambda_comp = delta_lambda_numerator /
685 delta_lambda_denominator;
686 // Lambda is real-valued
687 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 delta_u->zero();
692 delta_u->add(1., *z);
693 delta_u->add(-delta_lambda, *y);
694 delta_u->close();
695
696 // Update the system solution and the continuation parameter.
697 solution->add(1., *delta_u);
698 solution->close();
699 *continuation_parameter += delta_lambda;
700
701 // Did the Newton step actually reduce the residual?
703 assembly(true, // Residual
704 false); // Jacobian
705 rhs->close();
706 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 const Real norm_du_norm_R = delta_u->l2_norm() / nonlinear_residual_afterstep;
714 if (!quiet)
715 libMesh::out << " norm_du_norm_R=" << norm_du_norm_R << std::endl;
716
717
718 // Factor to decrease the stepsize by for backtracking
719 Real newton_stepfactor = 1.;
720
721 const bool attempt_backtracking =
722 (nonlinear_residual_afterstep > solution_tolerance)
723 && (nonlinear_residual_afterstep > nonlinear_residual_beforestep)
724 && (n_backtrack_steps>0)
725 && (norm_du_norm_R > 1.)
726 ;
727
728 // If residual is not reduced, do Newton back tracking.
729 if (attempt_backtracking)
730 {
731 if (!quiet)
732 libMesh::out << "Newton step did not reduce residual." << std::endl;
733
734 // back off the previous step.
735 solution->add(-1., *delta_u);
736 solution->close();
737 *continuation_parameter -= delta_lambda;
738
739 // Backtracking: start cutting the Newton stepsize by halves until
740 // the new residual is actually smaller...
741 for (unsigned int backtrack_step=0; backtrack_step<n_backtrack_steps; ++backtrack_step)
742 {
743 newton_stepfactor *= 0.5;
744
745 if (!quiet)
746 libMesh::out << "Shrinking step size by " << newton_stepfactor << std::endl;
747
748 // Take fractional step
749 solution->add(newton_stepfactor, *delta_u);
750 solution->close();
751 *continuation_parameter += newton_stepfactor*delta_lambda;
752
754 assembly(true, // Residual
755 false); // Jacobian
756 rhs->close();
757 nonlinear_residual_afterstep = rhs->l2_norm();
758
759 if (!quiet)
760 libMesh::out << "At shrink step "
761 << backtrack_step
762 << ", nonlinear_residual_afterstep="
763 << nonlinear_residual_afterstep
764 << std::endl;
765
766 if (nonlinear_residual_afterstep < nonlinear_residual_beforestep)
767 {
768 if (!quiet)
769 libMesh::out << "Backtracking succeeded!" << std::endl;
770
771 break; // out of backtracking loop
772 }
773
774 else
775 {
776 // Back off that step
777 solution->add(-newton_stepfactor, *delta_u);
778 solution->close();
779 *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 if ((attempt_backtracking) && (nonlinear_residual_afterstep > nonlinear_residual_beforestep))
790 {
791 //libMesh::err << "Backtracking failed." << std::endl;
792 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 if (newton_step<3)
799 {
800 solution->add(newton_stepfactor, *delta_u);
801 solution->close();
802 *continuation_parameter += newton_stepfactor*delta_lambda;
803 if (!quiet)
804 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 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 if (this->newton_progress_check)
821 {
822 if ((nonlinear_residual_afterstep > nonlinear_residual_firststep) &&
823 (newton_step+1 > static_cast<unsigned int>(0.5*newton_solver->max_nonlinear_iterations)))
824 {
825 libMesh::out << "Progress check failed: the current residual: "
826 << nonlinear_residual_afterstep
827 << ", is\n"
828 << "larger than the initial residual, and half of the allowed\n"
829 << "number of Newton iterations have elapsed.\n"
830 << "Exiting Newton iterations with converged==false." << std::endl;
831
832 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
838 {
839 libMesh::out << "Continuation parameter fell below min-allowable value." << std::endl;
840 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 if ( (max_continuation_parameter != 0.0) &&
846 {
847 libMesh::out << "Current continuation parameter value: "
849 << " exceeded max-allowable value."
850 << std::endl;
851 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 const Real norm_delta_u = delta_u->l2_norm();
858 const Real norm_u = solution->l2_norm();
859 libMesh::out << " delta_lambda = " << delta_lambda << std::endl;
860 libMesh::out << " newton_stepfactor*delta_lambda = " << newton_stepfactor*delta_lambda << std::endl;
861 libMesh::out << " lambda_current = " << *continuation_parameter << std::endl;
862 libMesh::out << " ||delta_u|| = " << norm_delta_u << std::endl;
863 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.
869 assembly(true, // Residual
870 false); // Jacobian
871 rhs->close();
872 const Real norm_residual = rhs->l2_norm();
873 libMesh::out << " ||R||_{L2} = " << norm_residual << std::endl;
874 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 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 (norm_residual < solution_tolerance))
884 {
885 if (!quiet)
886 libMesh::out << "Newton iterations converged!" << std::endl;
887
888 newton_converged = true;
889 break; // out of Newton iterations
890 }
891 } // end nonlinear loop
892
893 if (!newton_converged)
894 {
895 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 ds_current *= 0.5;
900
901 // Go back to previous solution and parameter value.
904
905 // Compute new predictor with smaller ds
907 }
908 else
909 {
910 // Set step convergence and break out
911 arcstep_converged=true;
912 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 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 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 libMesh::out.precision(old_precision);
927 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}
933
934
935
937{
938 // Solve for the updated tangent du1/ds, d(lambda1)/ds
940
941 // Advance the solution and the parameter to the next value.
943}
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
957{
958 // We shouldn't call this unless the current tangent already makes sense.
960
961 // Set pointer to underlying Newton solver
962 if (!newton_solver)
964 cast_ptr<NewtonSolver *> (this->time_solver->diff_solver().get());
965
966 // Assemble the system matrix AND rhs, with rhs = G_{\lambda}
967 this->rhs_mode = G_Lambda;
968
969 // Assemble Residual and Jacobian
970 this->assembly(true, // Residual
971 true); // Jacobian
972
973 // Not sure if this is really necessary
974 rhs->close();
975
976 // Solve G_u*y = G_{\lambda}
977 std::pair<unsigned int, Real> rval =
978 linear_solver->solve(*matrix,
979 *y,
980 *rhs,
981 1.e-12, // relative linear tolerance
982 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 if (!quiet)
988 libMesh::out << "G_u*y = G_{lambda} solver converged at step "
989 << rval.first
990 << " linear tolerance = "
991 << rval.second
992 << "."
993 << std::endl;
994
995 // Save old solution and parameter tangents for possible use in higher-order
996 // predictor schemes.
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 y->scale(-1.);
1025 const Real ynorm = y->l2_norm();
1026 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 *delta_u = *solution;
1032 delta_u->add(-1., *previous_u);
1033 delta_u->close();
1034
1035 const Real sgn_dlambda_ds =
1038
1039 if (sgn_dlambda_ds < 0.)
1040 {
1041 if (!quiet)
1042 libMesh::out << "dlambda_ds is negative." << std::endl;
1043
1044 dlambda_ds *= -1.;
1045 }
1046
1047 // Finally, set the new tangent vector, du/ds = dlambda/ds * y.
1048 du_ds->zero();
1049 du_ds->add(dlambda_ds, *y);
1050 du_ds->close();
1051
1052 if (!quiet)
1053 {
1054 libMesh::out << "d(lambda)/ds = " << dlambda_ds << std::endl;
1055 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 y->scale(-1.);
1060 y->close();
1061}
1062
1063
1064
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 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 if (!quiet)
1130 libMesh::out << "Setting Normalization Parameter Theta=" << Theta << std::endl;
1131}
1132
1133
1134
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 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 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 if (Theta_LOCA > 1.e8)
1176 {
1177 Theta_LOCA = 1.e8;
1178
1179 if (!quiet)
1180 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 if (!quiet)
1187 libMesh::out << "Setting Theta_LOCA=" << Theta_LOCA << std::endl;
1188}
1189
1190
1191
1193{
1194 // Set some stream formatting flags
1195 std::streamsize old_precision = libMesh::out.precision();
1197 libMesh::out.setf(std::ios_base::scientific);
1198
1199 // We must have a tangent that makes sense before we can update the solution.
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 y_old->close();
1209 y->close();
1210 const Real yoldnorm = y_old->l2_norm();
1211 const Real ynorm = y->l2_norm();
1212 const Number yoldy = y_old->dot(*y);
1213 const Real yold_over_y = yoldnorm/ynorm;
1214
1215 if (!quiet)
1216 {
1217 libMesh::out << "yoldnorm=" << yoldnorm << std::endl;
1218 libMesh::out << "ynorm=" << ynorm << std::endl;
1219 libMesh::out << "yoldy=" << yoldy << std::endl;
1220 libMesh::out << "yoldnorm/ynorm=" << yoldnorm/ynorm << std::endl;
1221 }
1222
1223 // Save the current value of ds before updating it
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 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 const Real double_threshold = 0.5;
1267 const Real halve_threshold = 0.5;
1268 if (yold_over_y > double_threshold)
1269 ds_current *= 2.;
1270 else if (yold_over_y < halve_threshold)
1271 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.
1277 {
1279 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 const Real newtonstep_growthfactor =
1288 static_cast<Real>(Nmax) / static_cast<Real>(newton_step+1);
1289
1290 if (!quiet)
1291 libMesh::out << "newtonstep_growthfactor=" << newtonstep_growthfactor << std::endl;
1292
1293 ds_current *= newtonstep_growthfactor;
1294 }
1295 }
1296
1297
1298 // Don't let the stepsize get above the user's maximum-allowed stepsize.
1299 if (ds_current > ds)
1300 ds_current = ds;
1301
1302 // Check also for a minimum allowed stepsize.
1303 if (ds_current < ds_min)
1304 {
1305 libMesh::out << "Enforcing minimum-allowed arclength stepsize of " << ds_min << std::endl;
1307 }
1308
1309 if (!quiet)
1310 {
1311 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 set_Theta();
1317
1318 // Also, recompute the LOCA scaling factor, which attempts to
1319 // maintain a reasonable value of dlambda/ds
1321
1322 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 *delta_u = *solution;
1327 delta_u->add(-1, *previous_u);
1328 delta_u->close();
1329 const Real normdu = delta_u->l2_norm();
1330 const Real C = (std::log (Theta_LOCA*normdu) /
1331 std::log (std::abs(*continuation_parameter-old_continuation_parameter))) - 1.0;
1332 if (!quiet)
1333 libMesh::out << "C=" << C << std::endl;
1334
1335 // Save the current value of u and lambda before updating.
1337
1338 if (!quiet)
1339 {
1340 libMesh::out << "Updating the solution with the tangent guess." << std::endl;
1341 libMesh::out << "||u_old||=" << this->solution->l2_norm() << std::endl;
1342 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.
1349
1350 if (!quiet)
1351 {
1352 libMesh::out << "||u_new||=" << this->solution->l2_norm() << std::endl;
1353 libMesh::out << "lambda_new=" << *continuation_parameter << std::endl;
1354 }
1355
1356 // Unset previous stream flags
1357 libMesh::out.precision(old_precision);
1358 libMesh::out.unsetf(std::ios_base::scientific);
1359}
1360
1361
1362
1363
1365{
1366 // Save the old solution vector
1367 *previous_u = *solution;
1368
1369 // Save the old value of lambda
1371}
1372
1373
1374
1376{
1377 if (predictor == Euler)
1378 {
1379 // 1.) Euler Predictor
1380 // Predict next the solution
1382 solution->close();
1383
1384 // Predict next parameter value
1386 }
1387
1388
1389 else if (predictor == AB2)
1390 {
1391 // 2.) 2nd-order explicit AB predictor
1392 libmesh_assert_not_equal_to (previous_ds, 0.0);
1393 const Real stepratio = ds_current/previous_ds;
1394
1395 // Build up next solution value.
1396 solution->add( 0.5*ds_current*(2.+stepratio), *du_ds);
1397 solution->add(-0.5*ds_current*stepratio , *previous_du_ds);
1398 solution->close();
1399
1400 // Next parameter value
1402 0.5*ds_current*((2.+stepratio)*dlambda_ds -
1403 stepratio*previous_dlambda_ds);
1404 }
1405
1406 else
1407 libmesh_error_msg("Unknown predictor!");
1408}
1409
1410} // namespace libMesh
std::ios_base::fmtflags setf(std::ios_base::fmtflags fmtfl)
Set the associated flags.
void unsetf(std::ios_base::fmtflags mask)
Clear the associated flags.
std::streamsize precision() const
Get the associated write precision.
Real previous_dlambda_ds
The old parameter tangent value.
Real ds
The initial arclength stepsize, selected by the user.
Real ds_current
Value of stepsize currently in use.
void save_current_solution()
Stores the current solution and continuation parameter (as "previous_u" and "old_continuation_paramet...
NumericVector< Number > * z
Temporary vector "z" ... the solution of .
void set_Theta_LOCA()
A centralized function for setting the other normalization parameter, i.e.
bool quiet
If quiet==false, the System prints extra information about what it is doing.
NewtonSolver * newton_solver
A pointer to the underlying Newton solver used by the DiffSystem.
NumericVector< Number > * delta_u
Temporary vector "delta u" ... the Newton step update in our custom augmented PDE solve.
Real min_continuation_parameter
The minimum-allowable value of the continuation parameter.
unsigned int n_arclength_reductions
Number of arclength reductions to try when Newton fails to reduce the residual.
virtual void solve() override
Perform a standard "solve" of the system, without doing continuation.
NumericVector< Number > * du_ds
Extra work vectors used by the continuation algorithm.
virtual void clear() override
Clear all the data structures associated with the system.
Real Theta
Arclength normalization parameter.
void apply_predictor()
Applies the predictor to the current solution to get a guess for the next solution.
void advance_arcstep()
Call this function after a continuation solve to compute the tangent and get the next initial guess.
void solve_tangent()
Special solve algorithm for solving the tangent system.
void update_solution()
This function (which assumes the most recent tangent vector has been computed) updates the solution a...
Real max_continuation_parameter
The maximum-allowable value of the continuation parameter.
ContinuationSystem(EquationSystems &es, const std::string &name, const unsigned int number)
Constructor.
Real previous_ds
The previous arcstep length used.
Real ds_min
The minimum-allowed steplength, defaults to 1.e-8.
void continuation_solve()
Perform a continuation solve of the system.
bool newton_progress_check
True by default, the Newton progress check allows the Newton loop to exit if half the allowed iterati...
bool tangent_initialized
False until initialize_tangent() is called.
double initial_newton_tolerance
How much to try to reduce the residual by at the first (inexact) Newton step.
Real dlambda_ds
The most recent value of the derivative of the continuation parameter with respect to s.
Real * continuation_parameter
The continuation parameter must be a member variable of the derived class, and the "continuation_para...
Real old_continuation_parameter
The system also keeps track of the old value of the continuation parameter.
unsigned int newton_step
Loop counter for nonlinear (Newton) iteration loop.
virtual void init_data() override
Initializes the member data fields associated with the system, so that, e.g., assemble() may be used.
double continuation_parameter_tolerance
How tightly should the Newton iterations attempt to converge delta_lambda.
void initialize_tangent()
Before starting arclength continuation, we need at least 2 prior solutions (both solution and u_previ...
@ AB2
Second-order explicit Adams-Bashforth predictor.
@ Euler
First-order Euler predictor.
double solution_tolerance
How tightly should the Newton iterations attempt to converge ||delta_u|| Defaults to 1....
Real newton_stepgrowth_aggressiveness
A measure of how rapidly one should attempt to grow the arclength stepsize based on the number of New...
NumericVector< Number > * previous_u
The solution at the previous value of the continuation variable.
NumericVector< Number > * previous_du_ds
The value of du_ds from the previous solution.
void set_Theta()
A centralized function for setting the normalization parameter theta.
unsigned int n_backtrack_steps
Another scaling parameter suggested by the LOCA people.
Real Theta_LOCA
Another normalization parameter, which is described in the LOCA manual.
NumericVector< Number > * y
Temporary vector "y" ... stores -du/dlambda, the solution of .
NumericVector< Number > * y_old
Temporary vector "y_old" ... stores the previous value of -du/dlambda, which is the solution of .
unsigned int max_linear_iterations
Each linear solver step should exit after max_linear_iterations is exceeded.
unsigned int max_nonlinear_iterations
The DiffSolver should exit in failure if max_nonlinear_iterations is exceeded and continue_after_max_...
virtual void clear() override
Clear all the data structures associated with the system.
Definition diff_system.C:64
std::unique_ptr< TimeSolver > time_solver
A pointer to the solver object we're going to use.
virtual void solve() override
Invokes the solver associated with the system.
This is the EquationSystems class.
NumericVector< Number > * rhs
The system matrix.
This class provides a specific system class.
Definition fem_system.h:55
virtual void assembly(bool get_residual, bool get_jacobian, bool apply_heterogeneous_constraints=false, bool apply_no_constraints=false) override
Prepares matrix or rhs for matrix assembly.
Definition fem_system.C:875
virtual void init_data() override
Initializes the member data fields associated with the system, so that, e.g., assemble() may be used.
Definition fem_system.C:868
SparseMatrix< Number > * matrix
The system matrix.
std::unique_ptr< LinearSolver< Number > > linear_solver
This class handles all the details of interfacing with various linear algebra packages like PETSc or ...
static std::unique_ptr< LinearSolver< T > > build(const libMesh::Parallel::Communicator &comm_in, const SolverPackage solver_package=libMesh::default_solver_package())
Builds a LinearSolver using the linear solver package specified by solver_package.
virtual void close()=0
Calls the NumericVector's internal assembly routines, ensuring that the values are consistent across ...
virtual T dot(const NumericVector< T > &v) const =0
virtual Real l2_norm() const =0
virtual void scale(const T factor)=0
Scale each element of the vector by the given factor.
virtual void zero()=0
Set all entries to zero.
virtual void add(const numeric_index_type i, const T value)=0
Adds value to the vector entry specified by i.
const Parallel::Communicator & comm() const
virtual void close()=0
Calls the SparseMatrix's internal assembly routines, ensuring that the values are consistent across p...
bool prefix_with_name() const
Definition system.h:1974
NumericVector< Number > & add_vector(std::string_view vec_name, const bool projections=true, const ParallelType type=PARALLEL)
Adds the additional vector vec_name to this system.
Definition system.C:756
std::unique_ptr< NumericVector< Number > > solution
Data structure to hold solution values.
Definition system.h:1655
std::string prefix() const
Definition system.h:1980
The libMesh namespace provides an interface to certain functionality in the library.
T libmesh_real(T a)
libmesh_assert(ctx)
OStreamProxy out
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real