Example codes
and tutorials
The (Not-so) Quick Guide
List of tutorials/demo codes
Single-Physics Problems
Poisson
Adaptivity illustrated for Poisson
Advection-Diffusion
Unsteady heat equation
Linear wave equation
The Young-Laplace equation
Navier-Stokes
Free-surface Navier-Stokes
Axisymmetric Navier-Stokes
Solid mechanics
Beam structures
Shell structures
Multi-physics Problems
Fluid-structure interaction
Boussinesq convection
Steady thermoelasticity
Methods-based example codes and tutorials
Mesh generation
Linear solvers and preconditioners
Visualisation of the results
Parallel processing
How to write a new element
How to write a new refineable element
Default nonlinear solvers -- the sequence of action functions
...
Documentation
FE theory and top-down discussion of the data structure
The (Not-so) Quick Guide
Comprehensive bottom-up discussion of the data structure
List of available structured and unstructured meshes
Linear solvers and preconditioners
Visualisation of the results
Parallel processing
Coding conventions and C++ style
Creating documentation
Optimisation - robustness vs. "raw speed"
Linear vs. nonlinear problems
Storing shape functions
Changing the default "full" integration scheme
Disabling the ALE formulation of unsteady equations
C vs. C++ output
Different sparse assembly techniques and the STL memory pool
Publications
Publications
Talks
Journal publications
Theses
Picture show
Download
Copyright
Download/installation instructions
Download page
FAQ & Contact
FAQ
Change log
Bugs and other known problems
Completeness of the library & our "To-Do List"
Contact the developers
Get involved

 


Beta release!

Please note that the library has not been "officially" released. While we continue to work on the documentation, these web pages are likely to contain broken links and documents in draft form. Please send an email to

oomph-lib AT maths DOT man DOT ac DOT uk

if you wish to be informed of the library's "official" release.

"Action" functions in oomph-lib's black-box Newton solver

oomph-lib's Problem class provides a black-box Newton solver, Problem::newton_solve(), which allows the driver code to be as compact as this:

   main()
   {
     // Create the problem object
     MyProblem problem;
     
     // Solve the problem, using oomph-lib's default Newton solver
     problem.newton_solve();
   }

While the availability of a black-box Newton solver is helpful, it is sometimes necessary to interact with the solution process, e.g. to perform certain additional tasks during the Newton iteration, or to analyse the convergence (or divergence!) of the iteration in more detail. For this purpose oomph-lib's Problem class has a number of empty virtual member functions that are executed at key stages of the Newton iteration. Two of these functions (Problem::actions_before_newton_solve() and Problem::actions_after_newton_solve() ) are pure virtual functions that must be implemented in every specific Problem, though they may, of course, be implemented as empty functions. Other functions may be overloaded to provide more detailed control.

The following table lists the main steps performed by oomph-lib's Newton solver and indicates at which point the various "action" functions are executed.

\[ \left. . \hspace{6cm} \right. \]

SEQUENCE OF MATHEMATICAL STEPS SEQUENCE OF FUNCTION CALLS / KEY CONTROL PARAMETERS
Step 1: Perform actions before solve.

\[ \left. . \hspace{6cm} \right. \]

Problem::actions_before_newton_solve();
Step 2: Initialise counter for number of Newton iterations.

\[ c=0 \]

unsigned count=0;
Start loop over Newton iterations
Step 3: Perform actions before the next Newton step

\[ \left. . \hspace{6cm}\right. \]

Problem::actions_before_newton_step();
Only during the first Newton step:
Step 4a: Perform actions before Newton convergence check

\[ \left. . \hspace{6cm} \right. \]

Problem::actions_before_newton_convergence_check();
Step 4b: Compute residual vector

\[ {\cal R}_i = {\cal R}_i\left(U_1,...,U_{N}\right) \mbox{\ \ \ \ for $i=1,...,N$} \]

Vector<double> residuals;
Problem::get_residuals(residuals); 
Step 4c: Convergence check. If converged go to Step 13

\[ \mbox{If $\max_i |{\cal R}_i| < {\tt tol} \Longrightarrow $ converged.} \]

...

// Check convergence
if (max_res<Problem::Newton_solver_tolerance)
{
   ...
}

... 
Step 5: Check if maximum residual or number of iterations exceed their maxima.

\[ \mbox{If \ \ } \left\{ \begin{array}{c} c > {\tt Max\_newton\_iterations} \\ \mbox{ or } \\ \max_i |{\cal R}_i| > {\tt Max\_residuals} \end{array} \right\} \Longrightarrow \mbox{diverged.} \]

...

// Check divergence/non-convergence
if ((max_res>Problem::Max_residuals) ||
    (count == Problem::Max_newton_iterations))
{
   ...

   // Throw an error
   throw NewtonSolverError(count,max_res);

}

... 
Step 6: Solve linear system for correction of unknowns

\[ \begin{array}{l} \mbox{Solve} \\ \\ \hspace{1cm}\sum_{j=1}^N {\cal J}_{ij} \ \delta U_j = {\cal R}_i \mbox{\ \ \ (for $i=1,...,N$)} \\ \\ \mbox{for $\delta U_j$ \ \ ($j=1,...,N$).} \\ \\ \mbox{Here the Jacobian matrix is given by} \\ \\ \hspace{1cm}{\cal J}_{ij} = \frac{\partial {\cal R}_i}{\partial U_j} \mbox{\ \ \ (for $i,j=1,...,N$)} \\ \end{array} \]

// Use the linear solver specified by 
// Problem::linear_solver_pt() to assemble
// and solve the linear system.
...
Step 7: Apply the corrections to the solution

\[ U_i := U_i - \delta U_i \mbox{\ \ \ (for $i=1,...,N$)} \\ \]

// Update the unknowns
...
Step 8: Perform actions after Newton step

\[ \left. . \hspace{6cm} \right. \]

Problem::actions_after_newton_step();
Step 9: Increment counter for number of Newton iterations

\[ c:=c+1 \]

count++;
Step 10: Perform actions before Newton convergence check

\[ \left. . \hspace{6cm} \right. \]

Problem::actions_before_newton_convergence_check();
Step 11: Compute residual vector

\[ {\cal R}_i = {\cal R}_i\left(U_1,...,U_{N}\right) \mbox{\ \ \ \ for $i=1,...,N$} \]

Vector<double> residuals;
Problem::get_residuals(residuals); 
Step 12: Convergence check. If converged go to Step 13, else go to Step 3.

\[ \mbox{If $\max_i |{\cal R}_i| < {\tt tol} \Longrightarrow$ converged.} \]

...

// Check convergence
if (max_res<Problem::Newton_solver_tolerance)
{
   ...
}

... 
End loop over Newton iterations
Step 13: Converged. Perform actions after solve.

\[ \left. . \hspace{6cm} \right. \]

Problem::actions_after_newton_solve();



The function Problem::actions_before_newton_solve() is often used to update boundary conditions when performing parameter studies in which these change; Problem::actions_after_newton_solve() may be used to automatically perform any post-processing when a solution has been obtained. The function Problem::actions_before_newton_convergence_check() makes it possible to update any "enslaved" problem parameters, i.e. parameters that depend on one or more of the unknowns in the problem but are not updated automatically when the unknown is changed. This arises most frequently in free-boundary problems in which the position of the nodes in the "bulk" mesh is determined by an algebraic node update procedure. When the Newton method updates the Data values that determine the shape of the domain boundary in Step 7, the nodal positions themselves are not updated automatically unless the node update function is executed in Problem::actions_before_newton_convergence_check(). See the discussion of the free-boundary Poisson problem for an example of its use.



Optimisation for linear problems.

Recall that, by default, oomph-lib regards all problems as nonlinear. Provided a good initial guess for the unknowns is available, the Newton solver will converge quadratically towards the solution. Within this framework, linear problems are simply special cases for which the Newton iteration converges in one step. However, if the problem is known to be linear, a few of the steps in the generic Newton iteration are unneccessary. For instance, it is not necessary to check the residual before or after the Newton step as we know that the exact solution will have been obtained (modulo roundoff errors) after step 7. The computation of the global residual vectors in steps 4 and 11 (which require a finite amount of cpu time) are therefore superfluous and are ommitted if the "user" declares a problem as linear by setting the flag
bool Problem::Problem_is_nonlinear
which is initialised to true in the constructor of the Problem base class to false.




PDF file

A pdf version of this document is available.
Generated on Mon Aug 10 11:48:41 2009 by  doxygen 1.4.7