system_selection.cpp system_data.hpp serif/backend/mfem/operators/volume_field_block.hpp algorithm limits utility serif/utils/error/errors.hpp serif serif::model serif::model::backend serif::model::backend::mfem serif::model::backend::mfem::detail #include"system_data.hpp" #include"serif/backend/mfem/operators/volume_field_block.hpp" #include<algorithm> #include<limits> #include<utility> #include"serif/utils/error/errors.hpp" namespaceserif::model::backend::mfem::detail{ namespace{ structSelectedRange{ intsource; intdestination; intsize; }; structSelectedContribution{ LinearizedBlockblock; std::vector<std::pair<int,int>>rows; std::vector<SelectedRange>columns; }; structSelection{ std::shared_ptr<constSystemData>data; std::vector<SelectedContribution>contributions; introws; intcolumns; }; [[nodiscard]]intselected_size(std::span<constCoordinateRange>ranges,intextent){ intsize=0; for(constCoordinateRangerange:ranges){ validate_range(range.offset,range.size,extent); utils::errors::serif_ensure<utils::errors::SERiFErrorCode::INVALID_DOF_COUNT>(range.size<=std::numeric_limits<int>::max()-size, "SelectedmodelcoordinatesexceedMFEM'sintegerindexrange.Selectfewerequationorstatecoordinates."); size+=range.size; } returnsize; } [[nodiscard]]std::vector<SelectedRange>selected_ranges(std::span<constCoordinateRange>ranges,intsource_offset,intsize){ std::vector<SelectedRange>result; intdestination=0; for(constCoordinateRangerange:ranges){ constintfirst=std::max(source_offset,range.offset); constintend=std::min(source_offset+size,range.offset+range.size); if(first<end){ result.push_back({first-source_offset,destination+first-range.offset,end-first}); } destination+=range.size; } returnresult; } voidadd_selection(Selection&selection,LinearizedBlockblock,intsource_column,std::span<constCoordinateRange>columns,std::vector<std::pair<int,int>>rows){ autoselected_columns=selected_ranges(columns,source_column,block.action()->Width()); if(!rows.empty()&&!selected_columns.empty()){ selection.contributions.push_back({std::move(block),std::move(rows),std::move(selected_columns)}); } } [[nodiscard]]std::shared_ptr<constSelection>make_selection(std::shared_ptr<constSystemData>data,std::span<constCoordinateRange>rows,std::span<constCoordinateRange>columns,boolcomplete_action){ autoselection=std::make_shared<Selection>(Selection{data,{},selected_size(rows,data->rows),selected_size(columns,data->columns)}); constauto&contributions=complete_action&&selection->data->complete_actions?*selection->data->complete_actions:selection->data->jacobian; for(constauto&contribution:contributions){ std::vector<std::pair<int,int>>selected_rows; for(constSelectedRangerange:selected_ranges(rows,contribution.row_offset,contribution.block.action()->Height())){ for(intindex=0;index<range.size;++index){ if(!selection->data->replaced_rows[contribution.row_offset+range.source+index]){ selected_rows.emplace_back(range.source+index,range.destination+index); } } } add_selection(*selection,contribution.block,contribution.column_offset,columns,std::move(selected_rows)); } for(constauto&replacement:selection->data->replacements){ std::vector<std::pair<int,int>>selected_rows; intdestination=0; for(constCoordinateRangerange:rows){ for(std::size_tindex=0;index<replacement.selected_rows.size();++index){ constintrow=replacement.selected_rows[index]; if(row>=range.offset&&row<range.offset+range.size){ selected_rows.emplace_back(static_cast<int>(index),destination+row-range.offset); } } destination+=range.size; } for(constauto&contribution:replacement.jacobian){ add_selection(*selection,contribution.block,contribution.column_offset,columns,selected_rows); } } returnselection; } voidapply_selection(constSelection&selection,const::mfem::Vector&input,::mfem::Vector&output,booltranspose,::mfem::Vector&direction,::mfem::Vector&result){ for(constauto&contribution:selection.contributions){ constauto&action=contribution.block.action(); direction.SetSize(transpose?action->Height():action->Width()); result.SetSize(transpose?action->Width():action->Height()); direction=0.0; result=0.0; if(transpose){ for(constauto[source,destination]:contribution.rows){ direction[source]+=input[destination]; } action->MultTranspose(direction,result); utils::errors::serif_ensure<utils::errors::SERiFErrorCode::MATRIX_SHAPE_ERROR>(result.Size()==action->Width(),"Aselectedmodeltransposechangeditsdeclaredoutputsize."); for(constSelectedRangerange:contribution.columns){ for(intindex=0;index<range.size;++index){ output[range.destination+index]+=result[range.source+index]; } } } else{ for(constSelectedRangerange:contribution.columns){ for(intindex=0;index<range.size;++index){ direction[range.source+index]+=input[range.destination+index]; } } action->Mult(direction,result); utils::errors::serif_ensure<utils::errors::SERiFErrorCode::MATRIX_SHAPE_ERROR>(result.Size()==action->Height(),"Aselectedmodelactionchangeditsdeclaredoutputsize."); for(constauto[source,destination]:contribution.rows){ output[destination]+=result[source]; } } } } voidapply_selection_many(constSelection&selection,std::span<const::mfem::Vector*const>inputs,std::span<::mfem::Vector*const>outputs,std::vector<::mfem::Vector>&directions,std::vector<::mfem::Vector>&results){ directions.resize(inputs.size()); results.resize(inputs.size()); std::vector<const::mfem::Vector*>active_directions; std::vector<::mfem::Vector*>active_results; std::vector<std::size_t>active; for(constauto&contribution:selection.contributions){ constauto&action=contribution.block.action(); active.clear(); active_directions.clear(); active_results.clear(); for(std::size_tk=0;k<inputs.size();++k){ auto&direction=directions[k]; direction.SetSize(action->Width()); direction=0.0; boolnonzero=false; for(constSelectedRangerange:contribution.columns){ for(intindex=0;index<range.size;++index){ direction[range.source+index]+=(*inputs[k])[range.destination+index]; nonzero=nonzero||direction[range.source+index]!=0.0; } } if(nonzero){ results[k].SetSize(action->Height()); results[k]=0.0; active.push_back(k); active_directions.push_back(&direction); active_results.push_back(&results[k]); } } if(active.empty()){ continue; } operators::backend::mfem::apply_many(*action,active_directions,active_results); for(conststd::size_tk:active){ utils::errors::serif_ensure<utils::errors::SERiFErrorCode::MATRIX_SHAPE_ERROR>(results[k].Size()==action->Height(),"Aselectedmodelactionchangeditsdeclaredoutputsize."); for(constauto[source,destination]:contribution.rows){ (*outputs[k])[destination]+=results[k][source]; } } } } [[nodiscard]]std::shared_ptr<const::mfem::SparseMatrix>assemble_selection(constSelection&selection){ std::vector<std::shared_ptr<const::mfem::SparseMatrix>>matrices; matrices.reserve(selection.contributions.size()); for(constauto&contribution:selection.contributions){matrices.push_back(checked_assembly(contribution.block));} //Destinationrow->(contribution,sourcerow)ininsertionorder. std::vector<int>offsets(static_cast<std::size_t>(selection.rows)+1,0); for(constauto&contribution:selection.contributions){ for(constauto[source,destination]:contribution.rows){++offsets[static_cast<std::size_t>(destination)+1];} } for(introw=0;row<selection.rows;++row){offsets[row+1]+=offsets[row];} std::vector<std::pair<int,int>>incidence(static_cast<std::size_t>(offsets.back())); { std::vector<int>cursor(offsets.begin(),offsets.end()-1); for(intindex=0;index<static_cast<int>(selection.contributions.size());++index){ for(constauto[source,destination]:selection.contributions[static_cast<std::size_t>(index)].rows){ incidence[static_cast<std::size_t>(cursor[static_cast<std::size_t>(destination)]++)]={index,source}; } } } structRowScratch{ std::vector<int>slot_of_column; std::vector<int>columns; std::vector<double>values; }; std::vector<std::vector<int>>row_columns(static_cast<std::size_t>(selection.rows)); std::vector<std::vector<double>>row_values(static_cast<std::size_t>(selection.rows)); std::vector<RowScratch>scratch(static_cast<std::size_t>(utils::parallel::worker_count())); utils::parallel::for_each_index(selection.rows,[&](constintrow,constintworker){ RowScratch&local=scratch[static_cast<std::size_t>(worker)]; local.slot_of_column.resize(static_cast<std::size_t>(selection.columns),-1); local.columns.clear(); local.values.clear(); for(intentry=offsets[row];entry<offsets[row+1];++entry){ constauto[index,source]=incidence[static_cast<std::size_t>(entry)]; constauto&matrix=*matrices[static_cast<std::size_t>(index)]; constauto&ranges=selection.contributions[static_cast<std::size_t>(index)].columns; constint*columns=matrix.GetRowColumns(source); constdouble*values=matrix.GetRowEntries(source); for(intk=0;k<matrix.RowSize(source);++k){ for(constSelectedRangerange:ranges){ constintcolumn=columns[k]-range.source; if(column>=0&&column<range.size){ int&slot=local.slot_of_column[static_cast<std::size_t>(range.destination+column)]; if(slot<0){ slot=static_cast<int>(local.columns.size()); local.columns.push_back(range.destination+column); local.values.push_back(0.0); } local.values[static_cast<std::size_t>(slot)]+=values[k]; } } } } auto&columns=row_columns[static_cast<std::size_t>(row)]; auto&values=row_values[static_cast<std::size_t>(row)]; for(std::size_tslot=local.columns.size();slot-->0;){ local.slot_of_column[static_cast<std::size_t>(local.columns[slot])]=-1; if(local.values[slot]==0.0){continue;} columns.push_back(local.columns[slot]); values.push_back(local.values[slot]); } }); returnoperators::backend::mfem::detail::make_csr(row_columns,row_values,selection.rows,selection.columns); } [[nodiscard]]intdiagonal_column(constSelectedContribution&contribution,introw){ for(constSelectedRangerange:contribution.columns){ if(row>=range.destination&&row<range.destination+range.size){ returnrange.source+row-range.destination; } } return-1; } [[nodiscard]]booluses_child_diagonal(constSelectedContribution&contribution){ for(constauto[source,destination]:contribution.rows){ constintcolumn=diagonal_column(contribution,destination); if(column>=0&&source!=column){ returnfalse; } } returncontribution.block.action()->Height()==contribution.block.action()->Width()&&contribution.block.has_diagonal(); } [[nodiscard]]std::vector<double>selection_diagonal(constSelection&selection){ std::vector<double>result(selection.rows,0.0); for(constauto&contribution:selection.contributions){ if(uses_child_diagonal(contribution)){ constautodiagonal=checked_diagonal(contribution.block); for(constauto[source,destination]:contribution.rows){ if(diagonal_column(contribution,destination)>=0){ result[destination]+=diagonal[source]; } } } else{ constautomatrix=checked_assembly(contribution.block); for(constauto[source,destination]:contribution.rows){ constintcolumn=diagonal_column(contribution,destination); if(column>=0){ for(intindex=0;index<matrix->RowSize(source);++index){ if(matrix->GetRowColumns(source)[index]==column){ result[destination]+=matrix->GetRowEntries(source)[index]; } } } } } } returnresult; } }//namespace LinearizedBlockselect_block(std::shared_ptr<constSystemData>data,std::span<constCoordinateRange>rows,std::span<constCoordinateRange>columns,boolcomplete_action){ constautoselection=make_selection(data,rows,columns,complete_action); constautometadata=complete_action&&data->complete_actions?make_selection(std::move(data),rows,columns,false):selection; constautoaction=operators::backend::mfem::linearized_action(selection->rows,selection->columns,[selection,direction=::mfem::Vector{},result=::mfem::Vector{}](constauto&input,auto&output)mutable{ apply_selection(*selection,input,output,false,direction,result); },[selection,direction=::mfem::Vector{},result=::mfem::Vector{}](constauto&input,auto&output)mutable{ apply_selection(*selection,input,output,true,direction,result); },[selection,directions=std::vector<::mfem::Vector>{},results=std::vector<::mfem::Vector>{}](std::span<const::mfem::Vector*const>inputs,std::span<::mfem::Vector*const>outputs)mutable{ apply_selection_many(*selection,inputs,outputs,directions,results); directions={}; results={}; }); LinearizedBlock::Diagonaldiagonal; LinearizedBlock::Assemblyassembly; if(selection->rows==selection->columns&&std::ranges::all_of(metadata->contributions,[](constauto&entry){ returnuses_child_diagonal(entry)||entry.block.has_assembly(); })){ diagonal=[metadata](){ returnselection_diagonal(*metadata); }; } if(std::ranges::all_of(metadata->contributions,[](constauto&entry){ returnentry.block.has_assembly(); })){ assembly=[metadata](){ returnassemble_selection(*metadata); }; } returnLinearizedBlock(action.action(),std::move(diagonal),std::move(assembly)); } }//namespaceserif::model::backend::mfem::detail