/******************************************************************************
* SOFA, Simulation Open-Framework Architecture *
* (c) 2006 INRIA, USTL, UJF, CNRS, MGH *
* *
* This program is free software; you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as published by *
* the Free Software Foundation; either version 2.1 of the License, or (at *
* your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, but WITHOUT *
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
* for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*******************************************************************************
* Authors: The SOFA Team and external contributors (see Authors.txt) *
* *
* Contact information: contact@sofa-framework.org *
******************************************************************************/
#pragma once
#include <sofa/component/linearsolver/iterative/ShewchukPCGLinearSolver.h>
#include <sofa/core/behavior/LinearSolver.h>
#include <sofa/component/linearsolver/iterative/MatrixLinearSolver.h>
#include <sofa/simulation/AnimateBeginEvent.h>
#include <sofa/helper/map.h>
#include <sofa/helper/AdvancedTimer.h>
#include <sofa/helper/ScopedAdvancedTimer.h>
#include <cmath>
namespace sofa::component::linearsolver::iterative
{
template<class TMatrix, class TVector>
ShewchukPCGLinearSolver<TMatrix,TVector>::ShewchukPCGLinearSolver()
: f_maxIter( initData(&f_maxIter,(unsigned)25,"iterations","maximum number of iterations of the Conjugate Gradient solution") )
, f_tolerance( initData(&f_tolerance,1e-5,"tolerance","desired precision of the Conjugate Gradient Solution (ratio of current residual norm over initial residual norm)") )
, f_use_precond( initData(&f_use_precond,true,"use_precond","Use preconditioner") )
, l_preconditioner(initLink("preconditioner", "Link towards the linear solver used to precondition the conjugate gradient"))
, f_update_step( initData(&f_update_step,(unsigned)1,"update_step","Number of steps before the next refresh of precondtioners") )
, f_build_precond( initData(&f_build_precond,true,"build_precond","Build the preconditioners, if false build the preconditioner only at the initial step") )
, f_graph( initData(&f_graph,"graph","Graph of residuals at each iteration") )
, next_refresh_step(0)
, newton_iter(0)
{
f_graph.setWidget("graph");
first = true;
this->f_listening.setValue(true);
}
template<class TMatrix, class TVector>
void ShewchukPCGLinearSolver<TMatrix,TVector>::init()
{
Inherit1::init();
// Find linear solvers
if (l_preconditioner.empty())
{
msg_info() << "Link \"preconditioner\" to the desired linear solver should be set to precondition the conjugate gradient.";
}
else
{
if (l_preconditioner.get() == nullptr)
{
msg_error() << "No preconditioner found at path: " << l_preconditioner.getLinkedPath();
sofa::core::objectmodel::BaseObject::d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid);
return;
}
else
{
if (l_preconditioner.get()->getTemplateName() == "GraphScattered")
{
msg_error() << "Can not use the preconditioner " << l_preconditioner.get()->getName() << " because it is templated on GraphScatteredType";
sofa::core::objectmodel::BaseObject::d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid);
return;
}
else
{
msg_info() << "Preconditioner path used: '" << l_preconditioner.getLinkedPath() << "'";
}
}
}
first = true;
sofa::core::objectmodel::BaseObject::d_componentState.setValue(sofa::core::objectmodel::ComponentState::Valid);
}
template<class TMatrix, class TVector>
void ShewchukPCGLinearSolver<TMatrix,TVector>::setSystemMBKMatrix(const core::MechanicalParams* mparams)
{
sofa::helper::AdvancedTimer::valSet("PCG::buildMBK", 1);
{
SCOPED_TIMER("PCG::setSystemMBKMatrix");
Inherit::setSystemMBKMatrix(mparams);
}
if (l_preconditioner.get()==nullptr) return;
if (first) //We initialize all the preconditioners for the first step
{
l_preconditioner.get()->setSystemMBKMatrix(mparams);
first = false;
next_refresh_step = 1;
}
else if (f_build_precond.getValue())
{
sofa::helper::AdvancedTimer::valSet("PCG::PrecondBuildMBK", 1);
SCOPED_TIMER_VARNAME(mbkTimer, "PCG::PrecondSetSystemMBKMatrix");
if (f_update_step.getValue()>0)
{
if (next_refresh_step>=f_update_step.getValue())
{
l_preconditioner.get()->setSystemMBKMatrix(mparams);
next_refresh_step=1;
}
else
{
next_refresh_step++;
}
}
}
l_preconditioner.get()->updateSystemMatrix();
}
template<>
inline void ShewchukPCGLinearSolver<component::linearsolver::GraphScatteredMatrix,component::linearsolver::GraphScatteredVector>::cgstep_beta(Vector& p, Vector& r, double beta)
{
p.eq(r,p,beta); // p = p*beta + r
}
template<>
inline void ShewchukPCGLinearSolver<component::linearsolver::GraphScatteredMatrix,component::linearsolver::GraphScatteredVector>::cgstep_alpha(Vector& x, Vector& p, double alpha)
{
x.peq(p,alpha); // x = x + alpha p
}
template<class Matrix, class Vector>
void ShewchukPCGLinearSolver<Matrix,Vector>::handleEvent(sofa::core::objectmodel::Event* event) {
/// this event shoul be launch before the addKToMatrix
if (sofa::simulation::AnimateBeginEvent::checkEventType(event))
{
newton_iter = 0;
std::map < std::string, sofa::type::vector<double> >& graph = * f_graph.beginEdit();
graph.clear();
}
}
template<class TMatrix, class TVector>
void ShewchukPCGLinearSolver<TMatrix,TVector>::solve (Matrix& M, Vector& x, Vector& b)
{
SCOPED_TIMER_VARNAME(solveTimer, "PCGLinearSolver::solve");
std::map < std::string, sofa::type::vector<double> >& graph = * f_graph.beginEdit();
// sofa::type::vector<double>& graph_error = graph["Error"];
newton_iter++;
char name[256];
sprintf(name,"Error %d",newton_iter);
'sprintf' is deprecated: This function is provided for compatibility reasons only. Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead.