amg.cpp serif/backend/mfem/solver/preconditioner/inverse/amg.hpp cmath serif/backend/mfem/solver/preconditioner/inverse/validation.hpp serif serif::solver serif::solver::preconditioner serif::solver::preconditioner::backend serif::solver::preconditioner::backend::mfem #include"serif/backend/mfem/solver/preconditioner/inverse/amg.hpp" #include<cmath> #include"serif/backend/mfem/solver/preconditioner/inverse/validation.hpp" #ifdefMFEM_USE_MPI #include"mfem/general/communication.hpp" #include"mfem/linalg/hypre.hpp" #endif namespaceserif::solver::preconditioner::backend::mfem { namespace { #ifdefMFEM_USE_MPI classAMGInversefinal:public::mfem::Operator { public: AMGInverse(const::mfem::SparseMatrix&matrix,constintcycles):Operator(matrix.Height()),m_matrix(matrix),m_partition{0,matrix.Height()}, m_hypre_matrix(MPI_COMM_SELF,matrix.Height(),m_partition,&m_matrix),m_solver(m_hypre_matrix),m_input(Height()) { m_solver.SetMaxIter(cycles); m_solver.SetTol(0.0); m_solver.SetPrintLevel(0); m_solver.iterative_mode=false; ::mfem::Vectorzero(Height()); zero=0.0; m_solver.Setup(zero,zero); } voidMult(const::mfem::Vector&input,::mfem::Vector&output)constoverride { detail::require_application_size(*this,input,output); const::mfem::Vector*source=&input; if(detail::vectors_overlap(input,output)) { m_input=input; source=&m_input; } output=0.0; m_solver.Mult(*source,output); } private: ::mfem::SparseMatrixm_matrix; HYPRE_BigIntm_partition[2]; ::mfem::HypreParMatrixm_hypre_matrix; ::mfem::HypreBoomerAMGm_solver; mutable::mfem::Vectorm_input; }; #endif [[nodiscard]]std::expected<Inverse,Failure>prepare_amg(conststd::shared_ptr<const::mfem::SparseMatrix>&matrix,constintcycles) { usingenumutils::errors::SERiFErrorCode; if(constautovalid=detail::check_square(matrix.get());!valid) { returnstd::unexpected(valid.error()); } if(!matrix->Finalized()) { returndetail::failure<PRECONDITIONER_INVALID_OPERATOR>("BoomerAMGrequiresafinalizedsparsematrix.Completeassemblybeforepreparingtheinverse."); } for(intindex=0;index<matrix->NumNonZeroElems();++index) { if(!std::isfinite(matrix->GetData()[index])) { returndetail::failure<PRECONDITIONER_NONFINITE_VALUE>("BoomerAMGsparseentry{}isnotfinite.",index); } } ::mfem::Vectordiagonal; matrix->GetDiag(diagonal); for(intindex=0;index<diagonal.Size();++index) { if(diagonal[index]<=0.0) { returndetail::failure<PRECONDITIONER_INVALID_OPERATOR>("BoomerAMGrequiresapositiveellipticdiagonal;entry{}is{}.Removeunsupportedzerorowsorchooseanotherinverse.",index,diagonal[index]); } } #ifdefMFEM_USE_MPI if(!::mfem::Mpi::IsInitialized()||::mfem::Mpi::IsFinalized()) { returndetail::failure<PRECONDITIONER_UNPREPARED>("InitializeanMPIsessionbeforeusingtheone-processBoomerAMGbackend.ThepreconditionerdoesnotownMPIlifetime."); } ::mfem::Hypre::Init(); returnInverse{.action=std::make_shared<constAMGInverse>(*matrix,cycles),.contract=ApplicationContract::fixed_linear}; #else (void)cycles; returndetail::failure<PRECONDITIONER_INCOMPATIBLE_STRATEGY>("BoomerAMGrequiresanMFEMbuildwithHypresupport.Chooseanotherinverseexplicitly;nonumericalfallbackisselectedautomatically."); #endif } }//namespace std::expected<Inverse,Failure>try_amg_inverse(std::shared_ptr<const::mfem::SparseMatrix>matrix,conststrategies::inverse::FixedAMG&options) { usingenumutils::errors::SERiFErrorCode; if(options.cycles<=0) { returndetail::failure<PRECONDITIONER_INVALID_OPTIONS>("FixedBoomerAMGneedsatleastonecycle,received{}.",options.cycles); } returnprepare_amg(matrix,options.cycles); } }//namespaceserif::solver::preconditioner::backend::mfem