lammps/lib/atc/NonLinearSolver.cpp

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