CppNoddy  0.92
All Classes Namespaces Files Functions Variables Typedefs Enumerator Friends Macros Modules Pages
Newton.cpp
Go to the documentation of this file.
1/// \file src/Newton.cpp
2/// Implementation of the vector NEWTON iteration class.
3/// This allows for Newton iteration to be performed
4/// for a vector function of a vector unknown.
5/// Use templates to allow double or complex.
6
7#include <complex>
8#include <string>
9
10#include <Newton.h>
11#include <Residual.h>
12#include <Exceptions.h>
13#include <DenseLinearSystem.h>
14#include <ArcLength_base.h>
15
16namespace CppNoddy {
17
18 template <typename _Type>
19 Newton<_Type>::Newton(Residual<_Type >* ptr_to_residual_object,
20 unsigned max_steps,
21 double tolerance,
22 double derivative_step)
23 : ArcLength_base< _Type >(),
24 TOL(tolerance),
25 MAX_STEPS(max_steps),
26 p_RESIDUAL(ptr_to_residual_object) {
27 p_RESIDUAL -> delta() = derivative_step;
28 DELTA = derivative_step;
29 MONITOR_DET = false;
30 LAST_DET_SIGN = 1;
31 }
32
33
34 template <typename _Type>
36 // length of the vector
37 unsigned N = x.size();
38 // Jacobian
39 DenseMatrix<_Type> J(N, N, 0.0);
40 // NVectors
41 DenseVector<_Type> oldFn(N, 0.0), newFn(N, 0.0);
42 // linear system object - native because no LAPACK complex at the moment
43 DenseLinearSystem<_Type> system(&J, &oldFn, "native");
44 system.set_monitor_det(MONITOR_DET);
45 // iteration counter
46 unsigned itn = 0;
47 do {
48 // increment the counter
49 ++itn;
50
51 // evaluate the residuals and jacobian at the current state
52 p_RESIDUAL -> update(x);
53 // get the residuals
54 oldFn = p_RESIDUAL -> residual();
55#ifdef DEBUG
56 std::cout << " DEBUG: starting with |Residuals| = " << oldFn.inf_norm() << "\n";
57#endif
58
59 if((std::abs(oldFn.inf_norm()) < TOL) || (itn == MAX_STEPS)) {
60 break;
61 }
62 // retrieve the current jacobian
63 J = p_RESIDUAL -> jacobian();
64
65 // linear solver
66 // \todo LU interface to LAPACK for complex matrices
67 system.solve();
68
69#ifdef DEBUG
70 std::cout << " DEBUG: Iteration number = " << itn << "\n";
71 std::cout << " DEBUG: |Newton correction| = " << oldFn.inf_norm() << "\n";
72#endif
73
74 // must *subtract* delta
75 x -= oldFn;
76 } while(true);
77
78 // More the 'MAX_STEPS' iterations currently triggers a failure.
79 if(itn == MAX_STEPS) {
80 std::string problem;
81 problem = " The Newton.iterate method took too many iterations. \n";
82 problem += " At the moment, this is set as a failure. \n";
83 throw ExceptionItn(problem, itn, oldFn.inf_norm());
84 }
85 LAST_DET_SIGN = system.get_det_sign();
86 if(MONITOR_DET) {
87 if(system.get_det_sign() * LAST_DET_SIGN < 0) {
88 std::string problem;
89 problem = "[ INFO ] : Determinant monitor has changed signs in ODE_BVP.\n";
90 problem += "[ INFO ] : Bifurcation detected.\n";
91 throw ExceptionBifurcation(problem);
92 }
93 }
94 }
95
96
97 template <typename _Type>
99#ifdef PARANOID
100 if(!this -> INITIALISED) {
101 std::string problem;
102 problem = "The Newton.arclength_solve method has been called, but \n";
103 problem += " you haven't called the arc_init method first. This means \n";
104 problem += " that a starting solution & derivatives thereof are unknown. \n";
105 problem += " Please initialise things appropriately! ";
106 throw ExceptionRuntime(problem);
107 }
108#endif
109#ifdef DEBUG
110 std::cout.precision(6);
111 std::cout << "[ DEBUG ] : Entering arclength_solve of the Newton class with\n";
112 std::cout << "[ DEBUG ] : a parameter of " << *(this -> p_PARAM) << "\n";
113#endif
114 // backup the state/system in case we fail
115 DenseVector<_Type> backup_state(x);
116 _Type backup_parameter(*(this -> p_PARAM));
117
118 int det_sign(1);
119 // init some local vars
120 bool step_succeeded(false);
121 unsigned itn(0);
122 // make a guess at the next solution
123 *(this -> p_PARAM) = this -> LAST_PARAM + this -> PARAM_DERIV_S * this -> DS;
124 x = this -> LAST_X + this -> X_DERIV_S * this -> DS;
125
126 // the order of the system
127 unsigned N = this -> p_RESIDUAL -> get_order();
128 DenseMatrix<_Type> J(N, N, 0.0);
129 DenseVector<_Type> R1(N, 0.0);
130 DenseVector<_Type> R2(N, 0.0);
131 DenseVector<_Type> dR_dp(N, 0.0);
132 //
133 do {
134 // update the residual object to the current guess
135 this -> p_RESIDUAL -> update(x);
136 // get the Jacobian of the system
137 J = this -> p_RESIDUAL -> jacobian();
138 R1 = this -> p_RESIDUAL -> residual();
139 // get the residual of the EXTRA arclength residual
140 double E1 = this -> arclength_residual(x);
141 // compute derivatives w.r.t the parameter
142 *(this -> p_PARAM) += this -> DELTA;
143 this -> p_RESIDUAL -> residual_fn(x, R2);
144 double E2 = this -> arclength_residual(x);
145 *(this -> p_PARAM) -= this -> DELTA;
146 dR_dp = (R2 - R1) / this -> DELTA;
147 _Type dE_dp = (E2 - E1) / this -> DELTA;
148 // bordering algorithm
149 DenseVector<_Type> y(-R1);
150 DenseVector<_Type> z(-dR_dp);
151#ifdef LAPACK
152 DenseLinearSystem<_Type> sys1(&J, &y, "lapack");
153#else
154 DenseLinearSystem<_Type> sys1(&J, &y, "native");
155#endif
156 sys1.set_monitor_det(MONITOR_DET);
157 try {
158 sys1.solve();
159 det_sign = sys1.get_det_sign();
160 } catch ( const ExceptionExternal &error ) {
161 break;
162 }
163 // the solve will have overwritten the LHS, so we need to replace it
164 J = this -> p_RESIDUAL -> jacobian();
165#ifdef LAPACK
166 DenseLinearSystem<_Type> sys2(&J, &z, "lapack");
167#else
168 DenseLinearSystem<_Type> sys2(&J, &z, "native");
169#endif
170 try {
171 sys2.solve();
172 } catch( const ExceptionExternal &error ) {
173 break;
174 }
175 DenseVector<_Type> JacE(this -> Jac_arclength_residual(x));
176 _Type delta_p = - (E1 + Utility::dot(JacE, y)) /
177 (dE_dp + Utility::dot(JacE, z));
178 DenseVector<_Type> delta_x = y + z * delta_p;
179 double max_correction = std::max(delta_x.inf_norm(), std::abs(delta_p));
180 if(max_correction < this -> TOL) {
181 step_succeeded = true;
182 break;
183 }
184 // add the corrections to the state variables
185 x += delta_x;
186 *(this -> p_PARAM) += delta_p;
187 ++itn;
188 if(itn > MAX_STEPS) {
189 step_succeeded = false;
190 break;
191 }
192 } while(true);
193
194 // is this a successful step?
195 if(!step_succeeded) {
196#ifdef DEBUG
197 std::cout << "[ DEBUG ] : REJECTING STEP \n";
198#endif
199 // if not a successful step then restore things
200 x = backup_state;
201 *(this -> p_PARAM) = backup_parameter;
202 // restore the residual object to its initial state
203 this -> p_RESIDUAL -> update(x);
204 // reduce our step length
205 this -> DS /= this -> ARCSTEP_MULTIPLIER;
206 } else {
207 // update the variables needed for arc-length continuation
208 this -> update(x);
209 if(LAST_DET_SIGN * det_sign < 0) {
210 LAST_DET_SIGN = det_sign;
211 std::string problem;
212 problem = "[ INFO ] : Determinant monitor has changed signs in the Newton class.\n";
213 problem += "[ INFO ] : Bifurcation detected.\n";
214 throw ExceptionBifurcation(problem);
215 } else {
216 LAST_DET_SIGN = det_sign;
217 }
218#ifdef DEBUG
219 std::cout << "[ DEBUG ] : Number of iterations = " << itn << "\n";
220 std::cout << "[ DEBUG ] : Parameter p = " << *(this -> p_PARAM)
221 << "; arclength DS = " << this -> DS << "\n";
222#endif
223 if(itn >= 7) {
224 // converging too slowly, so decrease DS
225 this -> DS /= this -> ARCSTEP_MULTIPLIER;
226#ifdef DEBUG
227 std::cout << "[ DEBUG ] : I decreased DS to " << this -> DS << "\n";
228#endif
229 }
230 if(itn <= 2) {
231 if(std::abs(this -> DS * this -> ARCSTEP_MULTIPLIER) < this -> MAX_DS) {
232 // converging too quickly, so increase DS
233 this -> DS *= this -> ARCSTEP_MULTIPLIER;
234#ifdef DEBUG
235 std::cout << "[ DEBUG ] : I increased DS to " << this -> DS << "\n";
236#endif
237 }
238 }
239 }
240 }
241
242 // the templated versions we require are:
243 template class Newton<double>
244 ;
245 template class Newton<std::complex<double> >
246 ;
247
248}
A base class for arclength-capable solvers.
Specification of the linear system class.
The collection of CppNoddy exceptions.
A vector NEWTON iteration class.
A specification of a (double/complex) VECTOR residual class.
A linear system class for vector right-hand sides.
void set_monitor_det(bool flag)
Store the sign of the determinant of the LHS matrix every time a solve is requested on a real system.
int get_det_sign() const
Get the sign of the determinant of the LHS matrix from the linear system just computed.
void solve()
Solve the sparse system.
A matrix class that constructs a DENSE matrix as a row major std::vector of DenseVectors.
Definition: DenseMatrix.h:25
An DenseVector class – a dense vector object.
Definition: DenseVector.h:34
double inf_norm() const
Infinity norm.
Definition: DenseVector.cpp:59
std::size_t size() const
A pass-thru definition to get the size of the vector.
Definition: DenseVector.h:330
An exception to indicate that an error has been detected in an external (LAPACK) routine.
Definition: Exceptions.h:20
An exception class that is thrown if too many Newton steps are taken in either the scalar or vector N...
Definition: Exceptions.h:90
A generic runtime exception.
Definition: Exceptions.h:158
A vector NEWTON iteration class.
Definition: Newton.h:25
void iterate(DenseVector< _Type > &x)
The Newton iteration method.
Definition: Newton.cpp:35
void arclength_solve(DenseVector< _Type > &x)
Arc-length solve the system.
Definition: Newton.cpp:98
Newton(Residual< _Type > *ptr_to_residual_object, unsigned max_steps=20, double tolerance=1.e-8, double derivative_step=1.e-8)
Constructor.
Definition: Newton.cpp:19
A base class to be inherited by objects that define residuals.
Definition: Residual.h:15
_Type dot(const DenseVector< _Type > &X, const DenseVector< _Type > &Y)
Templated dot product.
Definition: Utility.h:314
A collection of OO numerical routines aimed at simple (typical) applied problems in continuum mechani...

© 2012

R.E. Hewitt