replaced_rows.cpp serif/backend/mfem/operators/replaced_rows.hpp algorithm cmath utility serif/utils/error/errors.hpp serif serif::operators serif::operators::backend serif::operators::backend::mfem #include"serif/backend/mfem/operators/replaced_rows.hpp" #include<algorithm> #include<cmath> #include<utility> #include"serif/utils/error/errors.hpp" namespaceserif::operators::backend::mfem { namespace { structReplacedRowsfinal { LinearizedBlockoriginal; std::vector<int>selected; std::shared_ptr<const::mfem::SparseMatrix>replacement; }; classReplacedRowsOperatorfinal:public::mfem::Operator { public: explicitReplacedRowsOperator(std::shared_ptr<constReplacedRows>data) :::mfem::Operator(data->original.action()->Height(),data->original.action()->Width()),m_data(std::move(data)),m_input(Width()),m_boundary(m_data->replacement->Height()),m_dual(Height()) { } voidMult(const::mfem::Vector&input,::mfem::Vector&output)constoverride { usingnamespaceutils::errors; serif_ensure<SERiFErrorCode::INVALID_DOF_COUNT>(input.Size()==Width()&&output.Size()==Height(),"Arow-replacedphysicalblockrequiresinput/outputlengths{}/{},received{}/{}.",Width(),Height(),input.Size(),output.Size()); m_input=input; m_data->original.action()->Mult(m_input,output); m_data->replacement->Mult(m_input,m_boundary); for(introw=0;row<m_boundary.Size();++row) { output[m_data->selected[row]]=m_boundary[row]; } } voidMultTranspose(const::mfem::Vector&input,::mfem::Vector&output)constoverride { usingnamespaceutils::errors; serif_ensure<SERiFErrorCode::INVALID_DOF_COUNT>(input.Size()==Height()&&output.Size()==Width(),"Atransposedrow-replacedphysicalblockrequiresinput/outputlengths{}/{},received{}/{}.",Height(),Width(),input.Size(),output.Size()); m_dual=input; for(introw=0;row<m_boundary.Size();++row) { constintselected=m_data->selected[row]; m_boundary[row]=m_dual[selected]; m_dual[selected]=0.0; } m_data->original.action()->MultTranspose(m_dual,output); m_data->replacement->MultTranspose(m_boundary,m_input); output+=m_input; } private: std::shared_ptr<constReplacedRows>m_data; mutable::mfem::Vectorm_input,m_boundary,m_dual; }; [[nodiscard]]std::vector<double>replaced_diagonal(constReplacedRows&data) { usingnamespaceutils::errors; std::vector<double>diagonal=data.original.diagonal(); constintsize=std::min(data.original.action()->Height(),data.original.action()->Width()); serif_ensure<SERiFErrorCode::INVALID_DOF_COUNT>(diagonal.size()==static_cast<std::size_t>(size),"Theselectedphysicaldiagonalhas{}entries;itsrectangularself-columnrangerequires{}.",diagonal.size(),size); for(std::size_tindex=0;index<data.selected.size();++index) { constintrow=data.selected[index]; if(row<size) { diagonal[row]=(*data.replacement)(static_cast<int>(index),row); } } returndiagonal; } [[nodiscard]]std::shared_ptr<const::mfem::SparseMatrix>assemble_replaced_rows(constReplacedRows&data) { usingnamespaceutils::errors; constautooriginal=data.original.assemble(); serif_ensure<SERiFErrorCode::MATRIX_SHAPE_ERROR>(original!=nullptr&&original->Finalized()&&original->Height()==data.original.action()->Height()&&original->Width()==data.original.action()->Width(),"Theoriginalphysicalassemblermustreturnafinalizedmatrixmatchingitsacceptedaction."); autoresult=std::make_shared<::mfem::SparseMatrix>(original->Height(),original->Width()); std::vector<int>replacement_row(original->Height(),-1); for(std::size_tindex=0;index<data.selected.size();++index) { replacement_row[data.selected[index]]=static_cast<int>(index); } for(introw=0;row<original->Height();++row) { constboolreplaced=replacement_row[row]>=0; const::mfem::SparseMatrix&matrix=replaced?*data.replacement:*original; constintsource_row=replaced?replacement_row[row]:row; for(intentry=matrix.GetI()[source_row];entry<matrix.GetI()[source_row+1];++entry) { result->Add(row,matrix.GetJ()[entry],matrix.GetData()[entry]); } } result->Finalize(); returnresult; } }//namespace LinearizedBlockreplace_block_rows(LinearizedBlockoriginal,std::vector<int>selected_rows,std::shared_ptr<const::mfem::SparseMatrix>replacement) { usingnamespaceutils::errors; serif_ensure<SERiFErrorCode::INVALID_ARGUMENT>(replacement!=nullptr&&replacement->Finalized(),"Physicalrowreplacementrequiresafinalizedimmutablesparserowblock."); serif_ensure<SERiFErrorCode::MATRIX_SHAPE_ERROR>(selected_rows.size()==static_cast<std::size_t>(replacement->Height())&&replacement->Width()==original.action()->Width(),"Thereplacementmatrixmusthaveonerowperselectedresidualcoordinateandthesamephysicalcolumnsastheoriginalblock."); std::vector<bool>used(original.action()->Height(),false); for(introw:selected_rows) { serif_ensure<SERiFErrorCode::OUT_OF_RANGE_ERROR>(row>=0&&row<original.action()->Height(),"Replacementresidualrow{}liesoutsidethephysicalblockofheight{}.",row,original.action()->Height()); serif_ensure<SERiFErrorCode::INVALID_ARGUMENT>(!used[row],"Replacementresidualrow{}wasselectedmorethanonce.",row); used[row]=true; } for(intentry=0;entry<replacement->NumNonZeroElems();++entry) { serif_ensure<SERiFErrorCode::NON_FINITE_VALUE_ERROR>(std::isfinite(replacement->GetData()[entry]),"Aphysicalreplacementrowcontainsanonfiniteentry.Supplyafiniteacceptedlinearization."); } autodata=std::make_shared<constReplacedRows>(ReplacedRows{.original=std::move(original),.selected=std::move(selected_rows),.replacement=std::move(replacement)}); LinearizedBlock::Diagonaldiagonal; LinearizedBlock::Assemblyassembly; if(data->original.has_diagonal()) { diagonal=[data]() { returnreplaced_diagonal(*data); }; } if(data->original.has_assembly()) { assembly=[data]() { returnassemble_replaced_rows(*data); }; } returnLinearizedBlock(std::make_shared<ReplacedRowsOperator>(data),std::move(diagonal),std::move(assembly)); } }//namespaceserif::operators::backend::mfem