factorization.cpp serif/solver/preconditioner/backend/mfem/gravity/factorization.hpp cmath limits serif/solver/preconditioner/backend/mfem/inverse/validation.hpp serif serif::solver serif::solver::preconditioner serif::solver::preconditioner::backend serif::solver::preconditioner::backend::mfem #include"serif/solver/preconditioner/backend/mfem/gravity/factorization.hpp" #include<cmath> #include<limits> #include"serif/solver/preconditioner/backend/mfem/inverse/validation.hpp" namespaceserif::solver::preconditioner::backend::mfem { namespace { voidapply_inverse(const::mfem::Operator&inverse,const::mfem::Vector&input,::mfem::Vector&output) { output=0.0; inverse.Mult(input,output); } classGravityInversefinal:public::mfem::Operator { public: GravityInverse(Inversemass,Inversepositive_schur,OperatorHandledivergence,conststrategies::gravity::Factorizationmethod,constintsweeps,constdoublerelaxation):Operator(mass.action->Height()+positive_schur.action->Height()),m_mass(std::move(mass.action)),m_schur(std::move(positive_schur.action)), m_divergence(std::move(divergence)),m_method(method),m_sweeps(sweeps),m_relaxation(relaxation),m_input(Height()), m_gradient(m_mass->Height()),m_mass_correction(m_mass->Height()),m_potential(m_schur->Height()), m_schur_residual(sweeps>0?m_schur->Height():0),m_potential_correction(sweeps>0?m_schur->Height():0) { } voidMult(const::mfem::Vector&input,::mfem::Vector&output)constoverride { usingenumstrategies::gravity::Factorization; detail::require_application_size(*this,input,output); const::mfem::Vector*source=&input; if(detail::vectors_overlap(input,output)) { m_input=input; source=&m_input; } ::mfem::Vectorgradient_rhs(const_cast<double*>(source->GetData()),m_mass->Height()); ::mfem::Vectorpotential_rhs(const_cast<double*>(source->GetData())+m_mass->Height(),m_schur->Height()); ::mfem::Vectorgradient(output.GetData(),m_mass->Height()); ::mfem::Vectorpotential(output.GetData()+m_mass->Height(),m_schur->Height()); if(m_method==block_diagonal) { apply_inverse(*m_mass,gradient_rhs,gradient); apply_inverse(*m_schur,potential_rhs,potential); return; } if(m_method==upper_triangular) { m_potential=potential_rhs; m_potential*=-1.0; apply_inverse(*m_schur,m_potential,potential); m_divergence->MultTranspose(potential,m_gradient); m_gradient*=-1.0; m_gradient+=gradient_rhs; apply_inverse(*m_mass,m_gradient,gradient); return; } apply_inverse(*m_mass,gradient_rhs,gradient); m_divergence->Mult(gradient,m_potential); m_potential-=potential_rhs; apply_inverse(*m_schur,m_potential,potential); if(m_method==lower_triangular) { return; } refine_potential(potential); m_divergence->MultTranspose(potential,m_gradient); apply_inverse(*m_mass,m_gradient,m_mass_correction); gradient-=m_mass_correction; } private: voidrefine_potential(::mfem::Vector&potential)const { for(intsweep=0;sweep<m_sweeps;++sweep) { m_divergence->MultTranspose(potential,m_gradient); apply_inverse(*m_mass,m_gradient,m_mass_correction); m_divergence->Mult(m_mass_correction,m_schur_residual); m_schur_residual*=-1.0; m_schur_residual+=m_potential; apply_inverse(*m_schur,m_schur_residual,m_potential_correction); potential.Add(m_relaxation,m_potential_correction); } } OperatorHandlem_mass; OperatorHandlem_schur; OperatorHandlem_divergence; strategies::gravity::Factorizationm_method; intm_sweeps; doublem_relaxation; mutable::mfem::Vectorm_input; mutable::mfem::Vectorm_gradient; mutable::mfem::Vectorm_mass_correction; mutable::mfem::Vectorm_potential; mutable::mfem::Vectorm_schur_residual; mutable::mfem::Vectorm_potential_correction; }; }//namespace std::expected<Inverse,Failure>detail::prepare_gravity(Inversemass,Inversepositive_schur,OperatorHandledivergence,conststrategies::gravity::Factorizationmethod,constintcorrection_sweeps,constdoublerelaxation) { usingenumutils::errors::SERiFErrorCode; for(constauto*action:{mass.action.get(),positive_schur.action.get()}) { if(constautovalid=check_square(action);!valid) { returnstd::unexpected(valid.error()); } } if(!divergence||divergence->Width()!=mass.action->Height()||divergence->Height()!=positive_schur.action->Height()|| mass.action->Height()>std::numeric_limits<int>::max()-positive_schur.action->Height()) { returnfailure<PRECONDITIONER_SIZE_MISMATCH>("Mixedgravityrequiresdivergencerowsmatchingpotentialandcolumnsmatchinggravitygradient,withrepresentablecombinedsize."); } if(correction_sweeps<0||!std::isfinite(relaxation)||relaxation<=0.0) { returnfailure<PRECONDITIONER_INVALID_OPTIONS>("Mass-consistentgravityrefinementrequiresnonnegativesweepsandpositivefiniterelaxation."); } if((mass.contract!=ApplicationContract::fixed_linear&&mass.contract!=ApplicationContract::variable)|| (positive_schur.contract!=ApplicationContract::fixed_linear&&positive_schur.contract!=ApplicationContract::variable)) { returnfailure<PRECONDITIONER_INVALID_OPTIONS>("Mixedgravityrequireseachchildtodeclareafixedlinearorvariableapplicationcontract."); } constApplicationContractcontract=mass.contract==ApplicationContract::fixed_linear&&positive_schur.contract==ApplicationContract::fixed_linear?ApplicationContract::fixed_linear:ApplicationContract::variable; returnInverse{.action=std::make_shared<constGravityInverse>(std::move(mass),std::move(positive_schur),std::move(divergence),method,correction_sweeps,relaxation),.contract=contract}; } }//namespaceserif::solver::preconditioner::backend::mfem