oomph::BlackBoxFDNewtonSolver Namespace Reference

Namespace for black-box FD Newton solver. More...

Typedefs

typedef void(* ResidualFctPt) (const Vector< double > &parameters, const Vector< double > &unknowns, Vector< double > &residuals)
 
typedef void(* JacobianFctPt) (const Vector< double > &, const Vector< double > &, DenseDoubleMatrix &jacobian)
 

Functions

void black_box_fd_newton_solve (ResidualFctPt residual_fct, const Vector< double > &params, Vector< double > &unknowns, JacobianFctPt jacobian_fct)
 
void line_search (const Vector< double > &x_old, const double half_residual_squared_old, const Vector< double > &gradient, ResidualFctPt residual_fct, const Vector< double > &params, Vector< double > &newton_dir, Vector< double > &x, double &half_residual_squared, const double &stpmax)
 Line search helper for globally convergent Newton method. More...
 

Variables

unsigned Max_iter = 20
 Max. # of Newton iterations. More...
 
unsigned N_iter_taken = 0
 Number of Newton iterations taken in most recent invocation. More...
 
bool Doc_Progress = false
 
double FD_step = 1.0e-8
 FD step. More...
 
double Tol = 1.0e-8
 Tolerance. More...
 
bool Use_step_length_control = false
 Use steplength control do make globally convergent (default false) More...
 

Detailed Description

Namespace for black-box FD Newton solver.

/////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////

Typedef Documentation

◆ JacobianFctPt

typedef void(* oomph::BlackBoxFDNewtonSolver::JacobianFctPt) (const Vector< double > &, const Vector< double > &, DenseDoubleMatrix &jacobian)

◆ ResidualFctPt

typedef void(* oomph::BlackBoxFDNewtonSolver::ResidualFctPt) (const Vector< double > &parameters, const Vector< double > &unknowns, Vector< double > &residuals)

Function Documentation

◆ black_box_fd_newton_solve()

void oomph::BlackBoxFDNewtonSolver::black_box_fd_newton_solve ( ResidualFctPt  residual_fct,
const Vector< double > &  params,
Vector< double > &  unknowns,
JacobianFctPt  jacobian_fct 
)

Black-box FD Newton solver: Calling sequence for residual function is

residual_fct(parameters,unknowns,residuals)

where all arguments are double Vectors. unknowns.size() = residuals.size()

Reset number of Newton iterations taken in most recent invocation

71  {
72  // Jacobian, current and advanced residual Vectors
73  unsigned ndof = unknowns.size();
74  DenseDoubleMatrix jacobian(ndof);
75  Vector<double> residuals(ndof);
76  Vector<double> residuals_pls(ndof);
77  Vector<double> dx(ndof);
78  Vector<double> gradient(ndof);
79  Vector<double> newton_direction(ndof);
80 
81  double half_residual_squared = 0.0;
82  double max_step = 0.0;
83 
85  N_iter_taken = 0;
86 
87  // Newton iterations
88  for (unsigned iloop = 0; iloop < Max_iter; iloop++)
89  {
90  // Evaluate current residuals
91  residual_fct(params, unknowns, residuals);
92 
93  // Get half of squared residual and find maximum step length
94  // for step length control
96  {
97  half_residual_squared = 0.0;
98  double sum = 0.0;
99  for (unsigned i = 0; i < ndof; i++)
100  {
101  sum += unknowns[i] * unknowns[i];
102  half_residual_squared += residuals[i] * residuals[i];
103  }
104  half_residual_squared *= 0.5;
105  max_step = 100.0 * std::max(sqrt(sum), double(ndof));
106  }
107 
108 
109  // Check max. residuals
110  double max_res = std::fabs(*std::max_element(
111  residuals.begin(), residuals.end(), AbsCmp<double>()));
112 
113 
114  // Doc progress?
115  if (Doc_Progress)
116  {
117  oomph_info << "\nNewton iteration iter=" << iloop
118  << "\ni residual[i] unknown[i] " << std::endl;
119  for (unsigned i = 0; i < ndof; i++)
120  {
121  oomph_info << i << " " << residuals[i] << " " << unknowns[i]
122  << std::endl;
123  }
124  }
125 
126 
127  // Converged?
128  if (max_res < Tol)
129  {
130  return;
131  }
132 
133  // Next iteration...
134  N_iter_taken++;
135 
136  // ...and how would Sir like his Jacobian?
137  if (jacobian_fct == 0)
138  {
139  // FD loop for Jacobian
140  for (unsigned i = 0; i < ndof; i++)
141  {
142  double backup = unknowns[i];
143  unknowns[i] += FD_step;
144 
145  // Evaluate advanced residuals
146  residual_fct(params, unknowns, residuals_pls);
147 
148  // Do FD
149  for (unsigned j = 0; j < ndof; j++)
150  {
151  jacobian(j, i) = (residuals_pls[j] - residuals[j]) / FD_step;
152  }
153 
154  // Reset fd step
155  unknowns[i] = backup;
156  }
157  }
158  // Analytical Jacobian
159  else
160  {
161  jacobian_fct(params, unknowns, jacobian);
162  }
163 
164 
165  if (Doc_Progress)
166  {
167  oomph_info << "\n\nJacobian: " << std::endl;
168  jacobian.sparse_indexed_output(*(oomph_info.stream_pt()));
169  oomph_info << std::endl;
170  }
171 
172  // Get gradient
174  {
175  for (unsigned i = 0; i < ndof; i++)
176  {
177  double sum = 0.0;
178  for (unsigned j = 0; j < ndof; j++)
179  {
180  sum += jacobian(j, i) * residuals[j];
181  }
182  gradient[i] = sum;
183  }
184  }
185 
186  // Solve
187  jacobian.solve(residuals, newton_direction);
188 
189  // Update
191  {
192  for (unsigned i = 0; i < ndof; i++)
193  {
194  newton_direction[i] *= -1.0;
195  }
196  // Update with steplength control
197  Vector<double> unknowns_old(unknowns);
198  double half_residual_squared_old = half_residual_squared;
199  line_search(unknowns_old,
200  half_residual_squared_old,
201  gradient,
202  residual_fct,
203  params,
204  newton_direction,
205  unknowns,
206  half_residual_squared,
207  max_step);
208  }
209  else
210  {
211  // Direct Newton update:
212  for (unsigned i = 0; i < ndof; i++)
213  {
214  unknowns[i] -= newton_direction[i];
215  }
216  }
217  }
218 
219 
220  // Failed to converge
221  std::ostringstream error_stream;
222  error_stream << "Newton solver did not converge in " << Max_iter
223  << " steps " << std::endl;
224 
225  throw OomphLibError(
226  error_stream.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
227  }
AnnoyingScalar sqrt(const AnnoyingScalar &x)
Definition: AnnoyingScalar.h:134
int i
Definition: BiCGSTAB_step_by_step.cpp:9
std::ostream *& stream_pt()
Access function for the stream pointer.
Definition: oomph_definitions.h:464
#define max(a, b)
Definition: datatypes.h:23
dictionary params
Definition: Particles2023AnalysisHung.py:35
Real fabs(const Real &a)
Definition: boostmultiprec.cpp:117
unsigned Max_iter
Max. # of Newton iterations.
Definition: black_box_newton_solver.cc:44
unsigned N_iter_taken
Number of Newton iterations taken in most recent invocation.
Definition: black_box_newton_solver.cc:47
void line_search(const Vector< double > &x_old, const double half_residual_squared_old, const Vector< double > &gradient, ResidualFctPt residual_fct, const Vector< double > &params, Vector< double > &newton_dir, Vector< double > &x, double &half_residual_squared, const double &stpmax)
Line search helper for globally convergent Newton method.
Definition: black_box_newton_solver.cc:233
double Tol
Tolerance.
Definition: black_box_newton_solver.cc:57
double FD_step
FD step.
Definition: black_box_newton_solver.cc:54
bool Doc_Progress
Definition: black_box_newton_solver.cc:51
bool Use_step_length_control
Use steplength control do make globally convergent (default false)
Definition: black_box_newton_solver.cc:60
OomphInfo oomph_info
Definition: oomph_definitions.cc:319
#define OOMPH_EXCEPTION_LOCATION
Definition: oomph_definitions.h:61
#define OOMPH_CURRENT_FUNCTION
Definition: oomph_definitions.h:86
std::ptrdiff_t j
Definition: tut_arithmetic_redux_minmax.cpp:2

References Doc_Progress, boost::multiprecision::fabs(), FD_step, i, j, line_search(), max, Max_iter, N_iter_taken, OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, oomph::oomph_info, Particles2023AnalysisHung::params, oomph::DoubleMatrixBase::solve(), oomph::Matrix< T, MATRIX_TYPE >::sparse_indexed_output(), sqrt(), oomph::OomphInfo::stream_pt(), Tol, and Use_step_length_control.

Referenced by AxisymOscillatingDisk::AxisymOscillatingDisk(), oomph::SarahBL::exact_soln(), SarahBL::exact_soln(), oomph::SarahBL::full_exact_soln(), SarahBL::full_exact_soln(), and main().

◆ line_search()

void oomph::BlackBoxFDNewtonSolver::line_search ( const Vector< double > &  x_old,
const double  half_residual_squared_old,
const Vector< double > &  gradient,
ResidualFctPt  residual_fct,
const Vector< double > &  params,
Vector< double > &  newton_dir,
Vector< double > &  x,
double half_residual_squared,
const double stpmax 
)

Line search helper for globally convergent Newton method.

242  {
243  const double min_fct_decrease = 1.0e-4;
244  double convergence_tol_on_x = 1.0e-16;
245  double f_aux = 0.0;
246  double lambda_aux = 0.0;
247  double proposed_lambda = 0.0;
248  unsigned n = x_old.size();
249  double sum = 0.0;
250  for (unsigned i = 0; i < n; i++)
251  {
252  sum += newton_dir[i] * newton_dir[i];
253  }
254  sum = sqrt(sum);
255  if (sum > stpmax)
256  {
257  for (unsigned i = 0; i < n; i++)
258  {
259  newton_dir[i] *= stpmax / sum;
260  }
261  }
262  double slope = 0.0;
263  for (unsigned i = 0; i < n; i++)
264  {
265  slope += gradient[i] * newton_dir[i];
266  }
267  if (slope >= 0.0)
268  {
269  std::ostringstream error_stream;
270  error_stream << "Roundoff problem in lnsrch: slope=" << slope << "\n";
271  throw OomphLibError(
272  error_stream.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
273  }
274  double test = 0.0;
275  for (unsigned i = 0; i < n; i++)
276  {
277  double temp =
278  std::fabs(newton_dir[i]) / std::max(std::fabs(x_old[i]), 1.0);
279  if (temp > test) test = temp;
280  }
281  double lambda_min = convergence_tol_on_x / test;
282  double lambda = 1.0;
283  while (true)
284  {
285  for (unsigned i = 0; i < n; i++)
286  {
287  x[i] = x_old[i] + lambda * newton_dir[i];
288  }
289 
290  // Evaluate current residuals
291  Vector<double> residuals(n);
292  residual_fct(params, x, residuals);
293  half_residual_squared = 0.0;
294  for (unsigned i = 0; i < n; i++)
295  {
296  half_residual_squared += residuals[i] * residuals[i];
297  }
298  half_residual_squared *= 0.5;
299 
300  if (lambda < lambda_min)
301  {
302  for (unsigned i = 0; i < n; i++) x[i] = x_old[i];
303 
304  // Create an Oomph Lib warning
305  OomphLibWarning("Warning: Line search converged on x only!",
306  "BlackBoxFDNewtonSolver::line_search()",
308  return;
309  }
310  else if (half_residual_squared <=
311  half_residual_squared_old + min_fct_decrease * lambda * slope)
312  {
313  return;
314  }
315  else
316  {
317  if (lambda == 1.0)
318  {
319  proposed_lambda =
320  -slope / (2.0 * (half_residual_squared -
321  half_residual_squared_old - slope));
322  }
323  else
324  {
325  double r1 = half_residual_squared - half_residual_squared_old -
326  lambda * slope;
327  double r2 = f_aux - half_residual_squared_old - lambda_aux * slope;
328  double a_poly =
329  (r1 / (lambda * lambda) - r2 / (lambda_aux * lambda_aux)) /
330  (lambda - lambda_aux);
331  double b_poly = (-lambda_aux * r1 / (lambda * lambda) +
332  lambda * r2 / (lambda_aux * lambda_aux)) /
333  (lambda - lambda_aux);
334  if (a_poly == 0.0)
335  {
336  proposed_lambda = -slope / (2.0 * b_poly);
337  }
338  else
339  {
340  double discriminant = b_poly * b_poly - 3.0 * a_poly * slope;
341  if (discriminant < 0.0)
342  {
343  proposed_lambda = 0.5 * lambda;
344  }
345  else if (b_poly <= 0.0)
346  {
347  proposed_lambda =
348  (-b_poly + sqrt(discriminant)) / (3.0 * a_poly);
349  }
350  else
351  {
352  proposed_lambda = -slope / (b_poly + sqrt(discriminant));
353  }
354  }
355  if (proposed_lambda > 0.5 * lambda)
356  {
357  proposed_lambda = 0.5 * lambda;
358  }
359  }
360  }
361  lambda_aux = lambda;
362  f_aux = half_residual_squared;
363  lambda = std::max(proposed_lambda, 0.1 * lambda);
364  }
365  }
const unsigned n
Definition: CG3DPackingUnitTest.cpp:11
cout<< "The eigenvalues of A are:"<< endl<< ces.eigenvalues()<< endl;cout<< "The matrix of eigenvectors, V, is:"<< endl<< ces.eigenvectors()<< endl<< endl;complex< float > lambda
Definition: ComplexEigenSolver_compute.cpp:9
squared absolute sa ArrayBase::abs2 DOXCOMMA MatrixBase::cwiseAbs2 sa Eigen::abs2 DOXCOMMA Eigen::pow DOXCOMMA ArrayBase::square nearest sa Eigen::floor DOXCOMMA Eigen::ceil DOXCOMMA ArrayBase::round nearest integer not less than the given sa Eigen::floor DOXCOMMA ArrayBase::ceil not a number test
Definition: GlobalFunctions.h:109
list x
Definition: plotDoE.py:28
Definition: indexed_view.cpp:20

References boost::multiprecision::fabs(), i, lambda, max, n, OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, Particles2023AnalysisHung::params, sqrt(), Eigen::test, and plotDoE::x.

Referenced by black_box_fd_newton_solve().

Variable Documentation

◆ Doc_Progress

bool oomph::BlackBoxFDNewtonSolver::Doc_Progress = false

Flag to indicate if progress of Newton iteration is to be documented (defaults to false)

Referenced by black_box_fd_newton_solve(), and main().

◆ FD_step

◆ Max_iter

◆ N_iter_taken

unsigned oomph::BlackBoxFDNewtonSolver::N_iter_taken = 0

Number of Newton iterations taken in most recent invocation.

Referenced by black_box_fd_newton_solve(), and main().

◆ Tol

double oomph::BlackBoxFDNewtonSolver::Tol = 1.0e-8

Tolerance.

Referenced by black_box_fd_newton_solve().

◆ Use_step_length_control

bool oomph::BlackBoxFDNewtonSolver::Use_step_length_control = false

Use steplength control do make globally convergent (default false)

Referenced by black_box_fd_newton_solve().