solve.cpp serif/backend/mfem/solver/linear/solve.hpp algorithm chrono cmath limits stdexcept mfem/linalg/solvers.hpp serif/solver/linear/failure.hpp serif/backend/mfem/shared/finite.hpp serif/utils/error/errors.hpp serif serif::solver serif::solver::linear serif::solver::linear::backend serif::solver::linear::backend::mfem #include"serif/backend/mfem/solver/linear/solve.hpp" #include<algorithm> #include<chrono> #include<cmath> #include<limits> #include<stdexcept> #include"mfem/linalg/solvers.hpp" #include"serif/solver/linear/failure.hpp" #include"serif/backend/mfem/shared/finite.hpp" #include"serif/utils/error/errors.hpp" namespaceserif::solver::linear::backend::mfem{ namespace{ usingClock=std::chrono::steady_clock; classCountedOperatorfinal:public::mfem::Operator{ public: explicitCountedOperator(const::mfem::Operator&operation) :::mfem::Operator(operation.Height(),operation.Width()),m_operation(operation){} voidMult(const::mfem::Vector&input,::mfem::Vector&output)constoverride{ constautostart=Clock::now(); m_operation.Mult(input,output); ++applications; seconds+=std::chrono::duration<double>(Clock::now()-start).count(); utils::errors::serif_ensure<utils::errors::SERiFErrorCode::NON_FINITE_VALUE_ERROR, NonFiniteApplication>( utils::checks::is_finite(output),"AcheckedKrylovapplicationproducednonfinitecoefficients."); } mutablestd::uint64_tapplications{0}; mutabledoubleseconds{0.0}; private: const::mfem::Operator&m_operation; }; classCountedPreconditionerfinal:public::mfem::Solver{ public: CountedPreconditioner(const::mfem::Operator&operation,constCountedOperator&counted,::mfem::Solver&inverse) :::mfem::Solver(inverse.Height(),inverse.Width()),counted_inverse(inverse),m_operation(operation),m_counted(counted),m_inverse(inverse){} voidSetOperator(const::mfem::Operator&operation)override{ utils::errors::serif_ensure<utils::errors::SERiFErrorCode::SOLVER_ERROR>(&operation==&m_counted, "FGMRESattemptedtoreplacethecountedoperatorduringasolve.Prepareanewsolveforadifferentlinearization."); m_inverse.SetOperator(m_operation); } voidMult(const::mfem::Vector&input,::mfem::Vector&output)constoverride{ counted_inverse.Mult(input,output); } CountedOperatorcounted_inverse; private: const::mfem::Operator&m_operation; constCountedOperator&m_counted; ::mfem::Solver&m_inverse; }; [[nodiscard]]doubletrue_residual(const::mfem::Operator&operation,const::mfem::Vector&right_hand_side, const::mfem::Vector&correction,::mfem::Vector&work){ operation.Mult(correction,work); work*=-1.0; work+=right_hand_side; returnutils::checks::is_finite(work)?work.Norml2():std::numeric_limits<double>::quiet_NaN(); } } Reportsolve(FGMRESoptions,const::mfem::Operator&operation,::mfem::Solver&preconditioner, preconditioner::ApplicationContractcontract,const::mfem::Vector&right_hand_side, ::mfem::Vector&correction,Controlcontrol){ usingnamespaceutils::errors; options.validate(); control.validate(); serif_ensure<SERiFErrorCode::SOLVER_ERROR,std::invalid_argument>(operation.Height()>0&&operation.Height()==operation.Width()&& preconditioner.Height()==operation.Width()&&preconditioner.Width()==operation.Height()&& right_hand_side.Size()==operation.Height()&&correction.Size()==operation.Width(), "FGMRESrequiresanonemptysquareoperator,amatchinginverse,andmatchingright-hand-sideandcorrectionvectors."); serif_ensure<SERiFErrorCode::SOLVER_ERROR,std::invalid_argument>(contract==preconditioner::ApplicationContract::fixed_linear|| contract==preconditioner::ApplicationContract::variable,"FGMRESreceivedanunrecognizedpreconditionerapplicationcontract."); Reportreport{.control=control}; report.right_hand_side_norm=right_hand_side.Norml2(); if(!utils::checks::is_finite(right_hand_side)||!utils::checks::is_finite(correction)||!std::isfinite(report.right_hand_side_norm)){ report.status=Status::non_finite; report.true_residual_norm=std::numeric_limits<double>::quiet_NaN(); report.relative_true_residual_norm=report.true_residual_norm; returnreport; } constdoublethreshold=control.convergence_threshold(report.right_hand_side_norm); //CopytheRHSbecausecallersmaysupplyoverlappingvectorviews. const::mfem::Vectorrhs(right_hand_side); ::mfem::Vectorwork(operation.Height()); CountedOperatorcounted_operation(operation); CountedPreconditionercounted_preconditioner(operation,counted_operation,preconditioner); ::mfem::FGMRESSolversolver; solver.SetPreconditioner(counted_preconditioner); solver.SetOperator(counted_operation); solver.SetKDim(options.restart_length); solver.SetPrintLevel(options.print_level); solver.SetRelTol(0.0); solver.SetAbsTol(threshold); solver.SetMaxIter(control.maximum_iterations); solver.iterative_mode=true; constautostart=Clock::now(); boolapplication_failed=false; boolnonfinite_application=false; report.initial_residual_norm=report.right_hand_side_norm; try{ if(!std::ranges::all_of(correction,[](doublevalue){returnvalue==0.0;})){ doubleguess_residual=std::numeric_limits<double>::quiet_NaN(); try{ guess_residual=true_residual(counted_operation,rhs,correction,work); }catch(constNonFiniteApplication&){ //AfiniterecycledcorrectionmayoverflowthisnewJacobian;retryfromzero. } if(!std::isfinite(guess_residual)||guess_residual>report.right_hand_side_norm){ correction=0.0; }else{ report.initial_residual_norm=guess_residual; } } //Analreadyconvergedguessstillreceivestheindependentfinalcheckbelow.Inparticular, //theexactlyzeroRHSmustnotenteraKrylovnormalizationthatdividesbyitszeronorm. if(report.initial_residual_norm>threshold){ solver.Mult(rhs,correction); report.reported_residual_norm=solver.GetFinalNorm(); }else{ report.reported_residual_norm=report.initial_residual_norm; } report.true_residual_norm=std::numeric_limits<double>::quiet_NaN(); if(utils::checks::is_finite(correction)){ report.true_residual_norm=true_residual(counted_operation,rhs,correction,work); } }catch(constNonFiniteApplication&){ nonfinite_application=true; report.true_residual_norm=std::numeric_limits<double>::quiet_NaN(); }catch(constApplicationFailure&){ application_failed=true; report.true_residual_norm=std::numeric_limits<double>::quiet_NaN(); } report.iterations=static_cast<int>(counted_preconditioner.counted_inverse.applications); report.restarts=report.iterations>0?(report.iterations-1)/options.restart_length:0; if(nonfinite_application){ report.status=Status::non_finite; }elseif(application_failed){ report.status=Status::backend_failure; }elseif(!std::isfinite(report.true_residual_norm)||!std::isfinite(report.reported_residual_norm)){ report.status=Status::non_finite; }elseif(report.true_residual_norm<=threshold){ report.status=Status::converged; }elseif(report.iterations>=control.maximum_iterations&&!solver.GetConverged()){ report.status=Status::maximum_iterations; }else{ report.status=solver.GetConverged()?Status::backend_failure:Status::breakdown; } report.relative_true_residual_norm=report.true_residual_norm==0.0?0.0:std::numeric_limits<double>::infinity(); if(report.right_hand_side_norm>0.0){ report.relative_true_residual_norm=report.true_residual_norm/report.right_hand_side_norm; } report.operator_applications=counted_operation.applications; report.preconditioner_applications=counted_preconditioner.counted_inverse.applications; report.operator_seconds=counted_operation.seconds; report.preconditioner_seconds=counted_preconditioner.counted_inverse.seconds; report.solve_seconds=std::chrono::duration<double>(Clock::now()-start).count(); returnreport; } }