iMSTK
Interactive Medical Simulation Toolkit
imstkNewtonSolver.cpp
1 /*
2 ** This file is part of the Interactive Medical Simulation Toolkit (iMSTK)
3 ** iMSTK is distributed under the Apache License, Version 2.0.
4 ** See accompanying NOTICE for details.
5 */
6 
7 #include "imstkNewtonSolver.h"
8 #include "imstkConjugateGradient.h"
9 #include "imstkDirectLinearSolver.h"
10 #include "imstkLogger.h"
11 
12 #include <iostream>
13 
14 namespace imstk
15 {
16 template<>
17 NewtonSolver<SparseMatrixd>::NewtonSolver() :
18  m_linearSolver(std::make_shared<ConjugateGradient>()),
19  m_forcingTerm(0.9),
20  m_absoluteTolerance(1e-3),
21  m_relativeTolerance(1e-6),
22  m_gamma(0.9),
23  m_etaMax(0.9),
24  m_maxIterations(1),
25  m_useArmijo(true)
26 {
27 }
28 
29 template<>
30 NewtonSolver<Matrixd>::NewtonSolver() :
31  m_linearSolver(std::make_shared<DirectLinearSolver<Matrixd>>()),
32  m_forcingTerm(0.9),
33  m_absoluteTolerance(1e-3),
34  m_relativeTolerance(1e-6),
35  m_gamma(0.9),
36  m_etaMax(0.9),
37  m_maxIterations(1),
38  m_useArmijo(true)
39 {
40 }
41 
42 template<typename SystemMatrix>
43 void
45 {
46  if (!this->m_nonLinearSystem)
47  {
48  LOG(WARNING) << "NewtonMethod::solve - nonlinear system is not set to the nonlinear solver";
49  return;
50  }
51 
52  // Compute norms, set tolerances and other temporaries
53  double fnorm = this->m_nonLinearSystem->evaluateF(x, this->m_isSemiImplicit).norm();
54  double stopTolerance = m_absoluteTolerance + m_relativeTolerance * fnorm;
55 
56  m_linearSolver->setTolerance(stopTolerance);
57 
58  Vectord dx = x;
59 
60  const int maxIters = this->m_isSemiImplicit ? 1 : static_cast<int>(m_maxIterations);
61 
62  for (int i = 0; i < maxIters; ++i)
63  {
64  if (fnorm < stopTolerance)
65  {
66  return;
67  }
68  this->updateJacobian(x);
69 
70  m_linearSolver->solve(dx);
71  this->m_updateIterate(-dx, x);
72 
73  double newNorm = this->armijo(dx, x, fnorm);
74 
75  if (m_forcingTerm > 0.0 && newNorm > stopTolerance)
76  {
77  double ratio = newNorm / fnorm; // Ratio of successive residual norms
78  this->updateForcingTerm(ratio, stopTolerance, fnorm);
79 
80  // Reset tolerance in the linear solver according to the new forcing term
81  // to avoid over solving of the system.
82  m_linearSolver->setTolerance(m_forcingTerm);
83  }
84 
85  fnorm = newNorm;
86  }
87 }
88 
89 template<typename SystemMatrix>
90 void
92 {
93  if (!this->m_nonLinearSystem)
94  {
95  LOG(WARNING) << "NewtonMethod::solve - nonlinear system is not set to the nonlinear solver";
96  return;
97  }
98 
99  size_t iterNum;
100  const auto& u = this->m_nonLinearSystem->getUnknownVector();
101  Vectord du = u; // make this a class member in future
102  double error0 = MAX_D;
103 
104  double epsilon = m_relativeTolerance * m_relativeTolerance;
105  for (iterNum = 0; iterNum < m_maxIterations; ++iterNum)
106  {
107  double error = updateJacobian(u);
108 
109  if (iterNum == 0)
110  {
111  error0 = error;
112  }
113 
114  // std::cout << "Num. of Newton Iterations: " << iterNum << "\tError ratio: " << error/error0 << ", " << error << " " << error0 << std::endl;
115  if (error / error0 < epsilon && iterNum > 0)
116  {
117  // std::cout << "Num. of Newton Iterations: " << iterNum << "\tError ratio: " << error/error0 << ", " << error << " " << error0 << std::endl;
118  break;
119  }
120 
121  m_linearSolver->solve(du);
122  this->m_nonLinearSystem->m_FUpdate(du, this->m_isSemiImplicit);
123  }
124 
125  this->m_nonLinearSystem->m_FUpdatePrevState();
126 
127  if (iterNum == m_maxIterations && !this->m_isSemiImplicit)
128  {
129  LOG(WARNING) << "NewtonMethod::solve - The solver did not converge after max. iterations";
130  }
131 }
132 
133 template<typename SystemMatrix>
134 double
136 {
137  // Evaluate the Jacobian and sets the matrix
138  if (!this->m_nonLinearSystem)
139  {
140  LOG(WARNING) << "NewtonMethod::updateJacobian - nonlinear system is not set to the nonlinear solver";
141  return -1;
142  }
143 
144  // auto& A = this->m_nonLinearSystem->m_dF(x);
145  // if (A.innerSize() == 0)
146  // {
147  // LOG(WARNING) << "NewtonMethod::updateJacobian - Size of matrix is 0!";
148  // return -1;
149  // }
150  //
151  // auto& b = this->m_nonLinearSystem->m_F(x, this->m_isSemiImplicit);
152 
153  const auto& vecAndMat = this->m_nonLinearSystem->m_F_dF(x, this->m_isSemiImplicit);
154  auto& b = *vecAndMat.first;
155  auto& A = *vecAndMat.second;
156  if (A.innerSize() == 0)
157  {
158  LOG(WARNING) << "NewtonMethod::updateJacobian - Size of matrix is 0!";
159  return -1;
160  }
161 
162  auto linearSystem = std::make_shared<typename LinearSolverType::LinearSystemType>(A, b);
163  //linearSystem->setLinearProjectors(this->m_nonLinearSystem->getLinearProjectors()); /// \todo Left for near future reference. Clear in future.
164  m_linearSolver->setSystem(linearSystem);
165 
166  return std::sqrt(b.dot(b));
167 }
168 
169 template<typename SystemMatrix>
170 void
171 NewtonSolver<SystemMatrix>::updateForcingTerm(const double ratio, const double stopTolerance, const double fnorm)
172 {
173  double eta = m_gamma * ratio * ratio;
174  double forcingTermSqr = m_forcingTerm * m_forcingTerm;
175 
176  // Save guard to prevent the forcing term to become too small for far away iterates
177  if (m_gamma * forcingTermSqr > 0.1)
178  {
180  eta = std::max(eta, m_gamma * forcingTermSqr);
181  }
182 
183  m_forcingTerm = std::max(std::min(eta, m_etaMax), 0.5 * stopTolerance / fnorm);
184 }
185 
186 template<typename SystemMatrix>
187 // std::shared_ptr<NewtonSolver<SystemMatrix>::LinearSolverType>
188 auto
190 {
191  return m_linearSolver;
192 }
193 
194 template<typename SystemMatrix>
195 void
197 {
198  m_absoluteTolerance = aTolerance;
199 }
200 
201 template<typename SystemMatrix>
202 double
204 {
205  return m_absoluteTolerance;
206 }
207 
208 template class NewtonSolver<SparseMatrixd>;
209 template class NewtonSolver<Matrixd>;
210 } // namespace imstk
auto getLinearSolver() const -> std::shared_ptr< LinearSolverType >
Get LinearSolver.
void solveGivenState(Vectord &x) override
Solve the non linear system of equations G(x)=0 using Newton&#39;s method.
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic > Matrixd
A dynamic size matrix of doubles.
Definition: imstkMath.h:97
void setAbsoluteTolerance(const double aTolerance)
Get JacobianMatrix. Returns jacobian matrix.
Compound Geometry.
void updateForcingTerm(const double ratio, const double stopTolerance, const double fnorm)
Update forcing term according to Eisenstat-Walker criteria.
double updateJacobian(const Vectord &x)
Update jacobians.
Base class for linear solvers.
double getAbsoluteTolerance() const
Get AbsoluteTolerance. Returns current tolerance value.
Newton method. This version of the newton method is based on the work by Tim Kelly and others at NC S...