border.cpp serif/backend/mfem/solver/preconditioner/border.hpp algorithm cmath serif/backend/mfem/shared/finite.hpp limits utility vector mfem/linalg/densemat.hpp serif/backend/mfem/solver/preconditioner/inverse/dense.hpp serif/utils/error/errors.hpp serif/backend/mfem/solver/preconditioner/inverse/validation.hpp coupling_internal.hpp serif serif::solver serif::solver::preconditioner serif::solver::preconditioner::backend serif::solver::preconditioner::backend::mfem #include"serif/backend/mfem/solver/preconditioner/border.hpp" #include<algorithm> #include<cmath> #include"serif/backend/mfem/shared/finite.hpp" #include<limits> #include<utility> #include<vector> #include"mfem/linalg/densemat.hpp" #include"serif/backend/mfem/solver/preconditioner/inverse/dense.hpp" #include"serif/utils/error/errors.hpp" #include"serif/backend/mfem/solver/preconditioner/inverse/validation.hpp" #include"coupling_internal.hpp" namespaceserif::solver::preconditioner::backend::mfem { namespace { usingutils::errors::SERiFErrorCode; usingdetail::failure; voidapply_in_batches(const::mfem::Operator&action,const::mfem::Array<const::mfem::Vector*>&inputs,::mfem::Array<::mfem::Vector*>&outputs,constintbatch) { for(intfirst=0;first<inputs.Size();first+=batch) { constintcount=std::min(batch,inputs.Size()-first); ::mfem::Array<const::mfem::Vector*>in(count); ::mfem::Array<::mfem::Vector*>out(count); for(intk=0;k<count;++k) { in[k]=inputs[first+k]; out[k]=outputs[first+k]; } action.ArrayMult(in,out); } } }//namespace std::expected<Inverse,Failure>try_bordered_inverse(Inversefields,TwoBlockCouplingscouplings,OperatorHandleborder_diagonal,conststrategies::inverse::DenseDirect&options) { if(!std::isfinite(options.minimum_reciprocal_condition)||options.minimum_reciprocal_condition<0.0||options.minimum_reciprocal_condition>1.0||(fields.contract!=ApplicationContract::fixed_linear&&fields.contract!=ApplicationContract::variable)) { returnfailure<SERiFErrorCode::PRECONDITIONER_INVALID_OPTIONS>("Theborderinverserequiresavalidapplicationcontractandafiniteminimumreciprocalconditionin[0,1]."); } if(!fields.action||!border_diagonal||!couplings.first_from_second||!couplings.second_from_first) { returnfailure<SERiFErrorCode::PRECONDITIONER_INVALID_OPERATOR>("Aborderpreparationrequiresownedfield,diagonal,andcross-couplingoperators."); } constintfield_size=fields.action->Height(); constintborder_size=border_diagonal->Height(); if(field_size<=0||border_size<0||fields.action->Width()!=field_size||border_diagonal->Width()!=border_size||couplings.first_from_second->Height()!=field_size||couplings.first_from_second->Width()!=border_size||couplings.second_from_first->Height()!=border_size||couplings.second_from_first->Width()!=field_size) { returnfailure<SERiFErrorCode::PRECONDITIONER_SIZE_MISMATCH>("Theborderblocksmustdescribeonesquaretwo-groupsystemwithmatchingcrosscouplings."); } if(field_size>std::numeric_limits<int>::max()-border_size||(border_size>0&&(border_size>std::numeric_limits<int>::max()/border_size||field_size>std::numeric_limits<int>::max()/border_size))) { returnfailure<SERiFErrorCode::PRECONDITIONER_SIZE_MISMATCH>("ThebordermatrixorcachedresponsesexceedMFEM'sindexrange."); } if(border_size==0) { returnfields; } ::mfem::DenseMatrixschur(border_size); std::shared_ptr<::mfem::DenseMatrix>response; if(fields.contract==ApplicationContract::fixed_linear) { response=std::make_shared<::mfem::DenseMatrix>(field_size,border_size); } //Columnsareprobedinchunks:thecouplingactionsapplytoawholechunkatonce(ArrayMult;SERiF's //volumeactionsthentraversethemeshonceperchunk),andthefieldinversesolveseachchunkasone //batch,soasparseLUreadsitsfactoronceperchunkinsteadofoncepercolumn.32columnsiswherethe //batchedAcceleratesolvestopsgettingcheaperpercolumnonthebenchmarkpotentialblock.Thecoupling //actionstakesub-batchesof8:asfastas32,withoutholding32field-sizedprobesineverylayer. constexprintchunk=32; constexprintprobe_chunk=8; ::mfem::Vectordiagonal(border_size); conststd::size_twidth=static_cast<std::size_t>(std::min(chunk,border_size)); std::vector<::mfem::Vector>basis(width,::mfem::Vector(border_size)); std::vector<::mfem::Vector>field_rhs(width,::mfem::Vector(field_size)); std::vector<::mfem::Vector>field_action(width,::mfem::Vector(field_size)); std::vector<::mfem::Vector>coupling(width,::mfem::Vector(border_size)); for(intfirst=0;first<border_size;first+=chunk) { constintcount=std::min(chunk,border_size-first); ::mfem::Array<const::mfem::Vector*>unit(count),inputs(count),actions(count); ::mfem::Array<::mfem::Vector*>probes(count),outputs(count),couples(count); for(intk=0;k<count;++k) { constautoslot=static_cast<std::size_t>(k); basis[slot]=0.0; basis[slot][first+k]=1.0; unit[k]=&basis[slot]; probes[k]=&field_rhs[slot]; } apply_in_batches(*couplings.first_from_second,unit,probes,probe_chunk); for(intk=0;k<count;++k) { constautoslot=static_cast<std::size_t>(k); if(!utils::checks::is_finite(field_rhs[slot])) { returnfailure<SERiFErrorCode::PRECONDITIONER_NONFINITE_VALUE>("Theborder-to-fieldprobereturnedanonfinitevalue."); } field_action[slot]=0.0; inputs[k]=&field_rhs[slot]; outputs[k]=&field_action[slot]; } fields.action->ArrayMult(inputs,outputs); for(intk=0;k<count;++k) { constautoslot=static_cast<std::size_t>(k); if(!utils::checks::is_finite(field_action[slot])) { returnfailure<SERiFErrorCode::PRECONDITIONER_NONFINITE_VALUE>("Thefieldinversereturnedanonfiniteborderresponse."); } actions[k]=&field_action[slot]; couples[k]=&coupling[slot]; } apply_in_batches(*couplings.second_from_first,actions,couples,probe_chunk); for(intk=0;k<count;++k) { constintcolumn=first+k; constautoslot=static_cast<std::size_t>(k); constauto&action=field_action[slot]; border_diagonal->Mult(basis[slot],diagonal); if(!utils::checks::is_finite(coupling[slot])||!utils::checks::is_finite(diagonal)) { returnfailure<SERiFErrorCode::PRECONDITIONER_NONFINITE_VALUE>("AborderJacobianprobereturnedanonfinitevalue."); } if(response) { response->SetCol(column,action); } for(introw=0;row<border_size;++row) { schur(row,column)=diagonal[row]-coupling[slot][row]; } } } autoborder=try_dense_inverse(schur,options); if(!border) { returnstd::unexpected(std::move(border.error())); } returndetail::try_coupled_inverse(std::move(fields),std::move(*border),std::move(couplings),strategies::coupling::CouplingOrder::lower_diagonal_upper,std::move(response)); } }//namespaceserif::solver::preconditioner::backend::mfem