forked from lijiext/lammps
113 lines
3.2 KiB
C++
113 lines
3.2 KiB
C++
// ATC headers
|
|
#include "NonLinearSolver.h"
|
|
#include "LinearSolver.h"
|
|
#include "ATC_Error.h"
|
|
#include "LammpsInterface.h"
|
|
|
|
|
|
namespace ATC {
|
|
//===================================================================
|
|
// TangentOperator
|
|
//===================================================================
|
|
|
|
//===================================================================
|
|
// NonLinearSolver
|
|
//===================================================================
|
|
NonLinearSolver::NonLinearSolver(TangentOperator * f,
|
|
const BC_SET * bcs, const int dof,
|
|
bool parallel):
|
|
f_(f),
|
|
bcs_(bcs),
|
|
dof_(dof),
|
|
rNorm0P_(1.0),
|
|
tol_(1.e-10),
|
|
tolx_(1.e-8),
|
|
tol0_(1.e-6),
|
|
maxIterations_(20),
|
|
parallel_(parallel)
|
|
{
|
|
}
|
|
//--------------------------------------------------------------------
|
|
double NonLinearSolver::residual_norm(VECTOR & r)
|
|
{
|
|
|
|
if (bcs_) {
|
|
DENS_VEC R = r;
|
|
BC_SET::const_iterator itr;
|
|
for (itr = bcs_->begin(); itr != bcs_->end(); itr++) {
|
|
int i = itr->first;
|
|
R(i) = 0;
|
|
}
|
|
return R.norm();
|
|
}
|
|
else { return r.norm(); }
|
|
}
|
|
//--------------------------------------------------------------------
|
|
bool NonLinearSolver::solve(VECTOR & x)
|
|
{
|
|
f_->function(x, r_);
|
|
rNorm0_ = residual_norm(r_);
|
|
if (rNorm0_ < tol_*rNorm0P_) { // if a "solution" does pass here rNorm0_ will be too small to allow for convergence
|
|
return true; // note abs vs rel tol
|
|
}
|
|
if (rNorm0_ == 0.0) rNorm0_ = 1.0;
|
|
if (rNorm0_ < tol0_ ) rNorm0_ = rNorm0P_;
|
|
if (rNorm0P_ == 1.0) rNorm0P_ = rNorm0_;
|
|
rNormP_ = rNorm0_;
|
|
dx_.reset(r_.nRows()); // needs to be sized for linear solver
|
|
// newton's method
|
|
for (int iter = 0; iter < maxIterations_ ; iter++ ) {
|
|
// compute tangent
|
|
f_->tangent(x, r_, A_);
|
|
rNorm_ = residual_norm(r_);
|
|
rNorm_ /= rNorm0_;
|
|
if (rNorm_ < tol_) {
|
|
return true;
|
|
}
|
|
SPAR_MAT Asparse(A_);
|
|
LinearSolver linearSolver(Asparse, LinearSolver::AUTO_SOLVE, parallel_);
|
|
if (bcs_) {
|
|
linearSolver.allow_reinitialization();
|
|
linearSolver.initialize(bcs_);
|
|
if (iter > 0) linearSolver.set_homogeneous_bcs();
|
|
else { x.zero(); } // linear solve w/ bcs will replace guess
|
|
}
|
|
r_ *= -1;
|
|
linearSolver.solve(dx_,r_);
|
|
|
|
if (iter > 0 && rNorm_ > rNormP_) {
|
|
bool descent = line_search(x);
|
|
if (! descent ) {
|
|
// return false;
|
|
}
|
|
}
|
|
rNormP_ = rNorm_;
|
|
x += dx_;
|
|
}
|
|
stringstream ss;
|
|
ss << "WARNING NonLinearSolver: did not converge, iterations="<< maxIterations_ <<" error= " << rNorm_;
|
|
ATC::LammpsInterface::instance()->print_msg_once(ss.str());
|
|
|
|
return false;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
|
|
|
|
bool NonLinearSolver::line_search(VECTOR & x)
|
|
{
|
|
double rNormP = rNormP_;
|
|
double dxnorm = dx_.norm();
|
|
while ( dxnorm > tolx_) {
|
|
dx_ *= 0.5; // bisection
|
|
dxnorm = dx_.norm();
|
|
f_->function(x+dx_,r_);
|
|
rNorm_ = residual_norm(r_)/rNorm0_;
|
|
if (rNorm_ < rNormP) return true;
|
|
}
|
|
return false; // no descent
|
|
}
|
|
|
|
} // end namespace ATC
|