sparse_direct.cpp serif/backend/mfem/solver/preconditioner/inverse/sparse_direct.hpp algorithm cmath mutex vector serif/backend/mfem/solver/preconditioner/inverse/validation.hpp direct_lu.hpp serif::solver::preconditioner::backend::mfem::SparseDirectCache::State serif serif::solver serif::solver::preconditioner serif::solver::preconditioner::backend serif::solver::preconditioner::backend::mfem #include"serif/backend/mfem/solver/preconditioner/inverse/sparse_direct.hpp" #include<algorithm> #include<cmath> #include<mutex> #include<vector> #include"serif/backend/mfem/solver/preconditioner/inverse/validation.hpp" #include"direct_lu.hpp" namespaceserif::solver::preconditioner::backend::mfem { namespace { classSparseDirectInversefinal:public::mfem::Operator { public: SparseDirectInverse(constintsize,std::unique_ptr<detail::DirectLU>factor):Operator(size),m_factor(std::move(factor)){} voidMult(const::mfem::Vector&input,::mfem::Vector&output)constoverride { detail::require_application_size(*this,input,output); //Thefactorcopiestheright-handsidefirst,soinputandoutputmayoverlap. m_factor->solve(input.GetData(),output.GetData()); } voidArrayMult(const::mfem::Array<const::mfem::Vector*>&inputs,::mfem::Array<::mfem::Vector*>&outputs)constoverride { constintcount=inputs.Size(); if(count!=outputs.Size()||count==0) { Operator::ArrayMult(inputs,outputs); return; } conststd::size_tsize=static_cast<std::size_t>(height); std::vector<double>right(size*static_cast<std::size_t>(count)),solution(right.size()); for(intcolumn=0;column<count;++column) { detail::require_application_size(*this,*inputs[column],*outputs[column]); std::copy_n(inputs[column]->GetData(),size,right.data()+size*static_cast<std::size_t>(column)); } m_factor->solve_many(count,right.data(),solution.data()); for(intcolumn=0;column<count;++column) { std::copy_n(solution.data()+size*static_cast<std::size_t>(column),size,outputs[column]->GetData()); } } private: std::unique_ptr<detail::DirectLU>m_factor; }; }//namespace structSparseDirectCache::State { std::mutexmutex; std::shared_ptr<constdetail::DirectLUPattern>pattern; }; SparseDirectCache::SparseDirectCache():m_state(std::make_unique<State>()){} SparseDirectCache::~SparseDirectCache()=default; std::shared_ptr<constdetail::DirectLUPattern>SparseDirectCache::pattern(constintsize,constlong*column_starts,constint*row_indices,constdouble*values,int&status) { conststd::scoped_locklock(m_state->mutex); if(m_state->pattern&&m_state->pattern->matches(size,column_starts,row_indices)) { status=0; returnm_state->pattern; } autoanalyzed=detail::DirectLUPattern::analyze(size,column_starts,row_indices,values,status); if(analyzed){m_state->pattern=analyzed;} returnanalyzed; } std::string_viewsparse_direct_backend()noexcept{returndetail::direct_lu_backend();} boolsparse_direct_available()noexcept{returndetail::direct_lu_available();} std::expected<Inverse,Failure>try_sparse_direct_inverse(std::shared_ptr<const::mfem::SparseMatrix>matrix,conststrategies::inverse::SparseDirect&,SparseDirectCache*cache) { usingenumutils::errors::SERiFErrorCode; if(constautovalid=detail::check_square(matrix.get());!valid) { returnstd::unexpected(valid.error()); } if(!matrix->Finalized()) { returndetail::failure<PRECONDITIONER_INVALID_OPERATOR>("Asparsedirectinverserequiresafinalizedsparsematrix.Completeassemblybeforepreparingtheinverse."); } if(!sparse_direct_available()) { returndetail::failure<PRECONDITIONER_INCOMPATIBLE_STRATEGY>("Thesparsedirectinverseisunavailable(backend'{}';AccelerateneedsmacOS15.5ornewer).Rebuildwith-Dsparse_direct=accelerateorumfpack,orchooseanotherinverseexplicitly;nonumericalfallbackisselectedautomatically.", sparse_direct_backend()); } constintsize=matrix->Height(); constintnonzeros=matrix->NumNonZeroElems(); constint*row_starts=matrix->GetI(); constint*columns=matrix->GetJ(); constdouble*data=matrix->GetData(); for(intindex=0;index<nonzeros;++index) { if(!std::isfinite(data[index])) { returndetail::failure<PRECONDITIONER_NONFINITE_VALUE>("Sparsedirectinverseentry{}isnotfinite.",index); } } //Everybackendtakescompressedcolumns;transposeMFEM'scompressedrows. std::vector<long>column_starts(static_cast<std::size_t>(size)+1,0); for(intentry=0;entry<nonzeros;++entry){++column_starts[static_cast<std::size_t>(columns[entry])+1];} for(intcolumn=0;column<size;++column){column_starts[static_cast<std::size_t>(column)+1]+=column_starts[static_cast<std::size_t>(column)];} std::vector<int>row_indices(static_cast<std::size_t>(nonzeros)); std::vector<double>values(static_cast<std::size_t>(nonzeros)); std::vector<long>cursor(column_starts.begin(),column_starts.end()-1); for(introw=0;row<size;++row) { for(intentry=row_starts[row];entry<row_starts[row+1];++entry) { constlongslot=cursor[static_cast<std::size_t>(columns[entry])]++; row_indices[static_cast<std::size_t>(slot)]=row; values[static_cast<std::size_t>(slot)]=data[entry]; } } intstatus=0;//Accelerate:SparseStatus_t;UMFPACK:itsstatuscode autopattern=cache?cache->pattern(size,column_starts.data(),row_indices.data(),values.data(),status) :detail::DirectLUPattern::analyze(size,column_starts.data(),row_indices.data(),values.data(),status); autofactor=pattern?detail::DirectLU::factor(std::move(pattern),values.data(),status):nullptr; if(!factor) { returndetail::failure<PRECONDITIONER_INVALID_OPERATOR>("SparseLUfactorizationofthe{}-rowblockfailed({}status{}).Theblockissingularornotrepresentable;chooseanotherinverse.", size,sparse_direct_backend(),status); } returnInverse{.action=std::make_shared<constSparseDirectInverse>(size,std::move(factor)),.contract=ApplicationContract::fixed_linear}; } }//namespaceserif::solver::preconditioner::backend::mfem