dense.cpp serif/backend/mfem/solver/preconditioner/inverse/dense.hpp algorithm cmath vector 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/dense.hpp" #include<algorithm> #include<cmath> #include<vector> #include"serif/backend/mfem/solver/preconditioner/inverse/validation.hpp" namespaceserif::solver::preconditioner::backend::mfem { namespace { classDenseInversefinal:public::mfem::Operator { public: explicitDenseInverse(const::mfem::DenseMatrix&matrix):Operator(matrix.Height()),m_matrix(matrix),m_pivots(matrix.Height()),m_factors(m_matrix.Data(),m_pivots.data()),m_row_scales(matrix.Height()) { for(introw=0;row<Height();++row) { for(intcolumn=0;column<Width();++column) { m_row_scales[row]=std::max(m_row_scales[row],std::abs(matrix(row,column))); } if(m_row_scales[row]>0.0) { for(intcolumn=0;column<Width();++column) { m_matrix(row,column)/=m_row_scales[row]; } } } } [[nodiscard]]boolfactor() { returnm_factors.Factor(Height()); } [[nodiscard]]std::expected<double,Failure>inverse_norm(constdoublecommon_scale)const { usingenumutils::errors::SERiFErrorCode; ::mfem::DenseMatrixinverse(Height()); m_factors.GetInverseMatrix(Height(),inverse.Data()); doublemaximum=0.0; for(introw=0;row<Height();++row) { doublesum=0.0; for(intcolumn=0;column<Width();++column) { constdoubleentry=inverse(row,column)/m_row_scales[column]; if(!std::isfinite(entry)) { returndetail::failure<PRECONDITIONER_NONFINITE_VALUE>("Thedenseinverseisnotrepresentableinthesecoordinates.Rescalebeforepreparingit."); } sum+=std::abs(entry)*common_scale; } maximum=std::max(maximum,sum); } returnmaximum; } voidMult(const::mfem::Vector&input,::mfem::Vector&output)constoverride { solve(input,output,false); } voidMultTranspose(const::mfem::Vector&input,::mfem::Vector&output)constoverride { solve(input,output,true); } private: voidsolve(const::mfem::Vector&input,::mfem::Vector&output,constbooltranspose)const { detail::require_application_size(*this,input,output); detail::copy_coordinates(input,output); if(transpose) { m_factors.RightSolve(Height(),1,output.GetData()); scale_rows(output); } else { scale_rows(output); m_factors.Solve(Height(),1,output.GetData()); } } voidscale_rows(::mfem::Vector&values)const { for(intindex=0;index<Height();++index) { values[index]/=m_row_scales[index]; } } ::mfem::DenseMatrixm_matrix; std::vector<int>m_pivots; ::mfem::LUFactorsm_factors; std::vector<double>m_row_scales; }; }//namespace std::expected<Inverse,Failure>try_dense_inverse(const::mfem::DenseMatrix&matrix,conststrategies::inverse::DenseDirect&options) { usingenumutils::errors::SERiFErrorCode; if(constautovalid=detail::check_square(&matrix);!valid) { returnstd::unexpected(valid.error()); } if(!std::isfinite(options.minimum_reciprocal_condition)||options.minimum_reciprocal_condition<0.0||options.minimum_reciprocal_condition>1.0) { returndetail::failure<PRECONDITIONER_INVALID_OPTIONS>("Theminimumreciprocalconditionmustbefiniteandin[0,1],received{}.",options.minimum_reciprocal_condition); } doublescale=0.0; for(introw=0;row<matrix.Height();++row) { for(intcolumn=0;column<matrix.Width();++column) { if(!std::isfinite(matrix(row,column))) { returndetail::failure<PRECONDITIONER_NONFINITE_VALUE>("Denseentry({},{})isnotfinite;prepareafinitesmall-blockoperator.",row,column); } scale=std::max(scale,std::abs(matrix(row,column))); } } if(scale==0.0) { returndetail::failure<PRECONDITIONER_SINGULAR_OPERATOR>("Thedenseoperatoriszeroandcannotbeinverted."); } doublenorm=0.0; for(introw=0;row<matrix.Height();++row) { doublesum=0.0; for(intcolumn=0;column<matrix.Width();++column) { sum+=std::abs(matrix(row,column)/scale); } norm=std::max(norm,sum); } constautoinverse=std::make_shared<DenseInverse>(matrix); if(!inverse->factor()) { returndetail::failure<PRECONDITIONER_SINGULAR_OPERATOR>("PivotedLUfoundazeropivotinthedenseoperator.Removedependentconstraintsorchooseanonsingularblock."); } constautoinverse_norm=inverse->inverse_norm(scale); if(!inverse_norm) { returnstd::unexpected(inverse_norm.error()); } constdoublereciprocal_condition=(1.0/norm)/*inverse_norm; if(!std::isfinite(reciprocal_condition)||reciprocal_condition<options.minimum_reciprocal_condition) { returndetail::failure<PRECONDITIONER_ILL_CONDITIONED_OPERATOR>("Denseinfinity-normreciprocalcondition{}isbelowtherequired{}.Rescaleorrevisethecoupledconstraints.",reciprocal_condition,options.minimum_reciprocal_condition); } returnInverse{.action=inverse}; } }//namespaceserif::solver::preconditioner::backend::mfem