diff --git a/Sofa/Component/LinearSolver/Iterative/src/sofa/component/linearsolver/iterative/PCGLinearSolver.cpp b/Sofa/Component/LinearSolver/Iterative/src/sofa/component/linearsolver/iterative/PCGLinearSolver.cpp index f76c68e98f8..ce48d34185d 100644 --- a/Sofa/Component/LinearSolver/Iterative/src/sofa/component/linearsolver/iterative/PCGLinearSolver.cpp +++ b/Sofa/Component/LinearSolver/Iterative/src/sofa/component/linearsolver/iterative/PCGLinearSolver.cpp @@ -31,9 +31,13 @@ namespace sofa::component::linearsolver::iterative void registerPCGLinearSolver(sofa::core::ObjectFactory* factory) { factory->registerObjects(core::ObjectRegistrationData("Linear solver using the preconditioned conjugate gradient iterative algorithm.") - .add< PCGLinearSolver >()); + .add< PCGLinearSolver >(true) + .add< PCGLinearSolver, linearalgebra::FullVector > >() + .add< PCGLinearSolver >, linearalgebra::FullVector > >()); } template class SOFA_COMPONENT_LINEARSOLVER_ITERATIVE_API PCGLinearSolver; +template class SOFA_COMPONENT_LINEARSOLVER_ITERATIVE_API PCGLinearSolver< linearalgebra::CompressedRowSparseMatrix, linearalgebra::FullVector >; +template class SOFA_COMPONENT_LINEARSOLVER_ITERATIVE_API PCGLinearSolver< linearalgebra::CompressedRowSparseMatrix >, linearalgebra::FullVector >; } // namespace sofa::component::linearsolver::iterative diff --git a/Sofa/Component/LinearSolver/Iterative/src/sofa/component/linearsolver/iterative/PCGLinearSolver.h b/Sofa/Component/LinearSolver/Iterative/src/sofa/component/linearsolver/iterative/PCGLinearSolver.h index 6c1d2b04316..345e1a6bc5c 100644 --- a/Sofa/Component/LinearSolver/Iterative/src/sofa/component/linearsolver/iterative/PCGLinearSolver.h +++ b/Sofa/Component/LinearSolver/Iterative/src/sofa/component/linearsolver/iterative/PCGLinearSolver.h @@ -54,6 +54,7 @@ class PCGLinearSolver : public sofa::component::linearsolver::MatrixLinearSolver SingleLink l_preconditioner; ///< Link towards the linear solver used to precondition the conjugate gradient core::objectmodel::lifecycle::DeprecatedData d_update_step; ///< Number of steps before the next refresh of preconditioners + void invert(Matrix& M) override; void solve (Matrix& M, Vector& x, Vector& b) override; void init() override; void bwdInit() override; @@ -102,6 +103,8 @@ inline void PCGLinearSolver; +extern template class SOFA_COMPONENT_LINEARSOLVER_ITERATIVE_API PCGLinearSolver< linearalgebra::CompressedRowSparseMatrix, linearalgebra::FullVector >; +extern template class SOFA_COMPONENT_LINEARSOLVER_ITERATIVE_API PCGLinearSolver< linearalgebra::CompressedRowSparseMatrix >, linearalgebra::FullVector >; #endif // !defined(SOFA_COMPONENT_LINEARSOLVER_ITERATIVE_PCGLINEARSOLVER_CPP) } // namespace sofa::component::linearsolver::iterative diff --git a/Sofa/Component/LinearSolver/Iterative/src/sofa/component/linearsolver/iterative/PCGLinearSolver.inl b/Sofa/Component/LinearSolver/Iterative/src/sofa/component/linearsolver/iterative/PCGLinearSolver.inl index 54317064ae6..7948c85c649 100644 --- a/Sofa/Component/LinearSolver/Iterative/src/sofa/component/linearsolver/iterative/PCGLinearSolver.inl +++ b/Sofa/Component/LinearSolver/Iterative/src/sofa/component/linearsolver/iterative/PCGLinearSolver.inl @@ -75,6 +75,14 @@ void PCGLinearSolver::init() this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid); return; } + else if (!std::is_same_v + && !dynamic_cast(l_preconditioner.get())) + { + msg_error() << "Cannot use the preconditioner " << l_preconditioner->getName() + << " because it is not templated on " << this->getTemplateName(); + this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid); + return; + } else { msg_info() << "Preconditioner path used: '" << l_preconditioner.getLinkedPath() << "'"; @@ -97,6 +105,8 @@ void PCGLinearSolver::init() template void PCGLinearSolver::ensureRequiredLinearSystemType() { + // only the matrix-free version requires a specific linear system + if constexpr (std::is_same_v) if (this->l_linearSystem) { auto* preconditionedMatrix = @@ -118,7 +128,8 @@ void PCGLinearSolver::bwdInit() if (this->isComponentStateInvalid()) return; - //link the linear systems of both the preconditioner and the PCG + //link the linear systems of both the preconditioner and the PCG (matrix-free version only) + if constexpr (std::is_same_v) if (l_preconditioner && this->l_linearSystem) { if (auto* preconditionerLinearSystem = l_preconditioner->getLinearSystem()) @@ -167,7 +178,21 @@ template void PCGLinearSolver::checkLinearSystem() { // a PreconditionedMatrixFreeSystem component is created in the absence of a linear system - this->template doCheckLinearSystem >(); + if constexpr (std::is_same_v) + this->template doCheckLinearSystem >(); + else + Inherit::checkLinearSystem(); // assembled version: a MatrixLinearSystem +} + +template +void PCGLinearSolver::invert(Matrix& M) +{ + // assembled version: the preconditioner is built on the matrix of the PCG, once per assembly + if constexpr (!std::is_same_v) + { + if (auto* preconditioner = dynamic_cast(l_preconditioner.get()); preconditioner && d_use_precond.getValue()) + preconditioner->invert(M); + } } template @@ -191,6 +216,24 @@ void PCGLinearSolver::solve (Matrix& M, Vector& x, Vector& b) const bool apply_precond = l_preconditioner.get()!=nullptr && d_use_precond.getValue(); + // z = P^-1 rhs + const auto applyPreconditioner = [this, &M](Vector& z, Vector& rhs) + { + SCOPED_TIMER_VARNAME(applyPrecondTimer, "PCGLinearSolver::apply Precond"); + if constexpr (std::is_same_v) + { + l_preconditioner->getLinearSystem()->setSystemSolution(z); + l_preconditioner->getLinearSystem()->setRHS(rhs); + l_preconditioner->solveSystem(); + l_preconditioner->getLinearSystem()->dispatchSystemSolution(z); + } + else // assembled version: the preconditioner works directly on the matrix of the PCG + { + z.resize(rhs.size()); + dynamic_cast(l_preconditioner.get())->solve(M, z, rhs); + } + }; + const Real b_norm = b.dot(b); const Real tol = d_tolerance.getValue() * b_norm; @@ -199,11 +242,7 @@ void PCGLinearSolver::solve (Matrix& M, Vector& x, Vector& b) if (apply_precond) { - SCOPED_TIMER_VARNAME(applyPrecondTimer, "PCGLinearSolver::apply Precond"); - l_preconditioner->getLinearSystem()->setSystemSolution(w); - l_preconditioner->getLinearSystem()->setRHS(r); - l_preconditioner->solveSystem(); - l_preconditioner->getLinearSystem()->dispatchSystemSolution(w); + applyPreconditioner(w, r); } else { @@ -225,11 +264,7 @@ void PCGLinearSolver::solve (Matrix& M, Vector& x, Vector& b) if (apply_precond) { - SCOPED_TIMER_VARNAME(applyPrecondTimer, "PCGLinearSolver::apply Precond"); - l_preconditioner->getLinearSystem()->setSystemSolution(s); - l_preconditioner->getLinearSystem()->setRHS(r); - l_preconditioner->solveSystem(); - l_preconditioner->getLinearSystem()->dispatchSystemSolution(s); + applyPreconditioner(s, r); } else {