volume_application.hpp algorithm array functional span tuple vector serif/backend/mfem/operators/volume_inputs.hpp serif/backend/mfem/operators/element_evaluation.hpp serif/backend/mfem/operators/element_transpose.hpp serif/backend/mfem/shared/parallel.hpp src/include/serif/backend/mfem/model/volume.hpp src/include/serif/backend/mfem/model/volume_actions.hpp src/include/serif/backend/mfem/operators/jacobian_operator.hpp src/include/serif/backend/mfem/operators/volume_field_block.hpp serif serif::operators serif::operators::backend serif::operators::backend::mfem #pragmaonce #include<algorithm> #include<array> #include<functional> #include<span> #include<tuple> #include<vector> #include"serif/backend/mfem/operators/volume_inputs.hpp" #include"serif/backend/mfem/operators/element_evaluation.hpp" #include"serif/backend/mfem/operators/element_transpose.hpp" #include"serif/backend/mfem/shared/parallel.hpp" namespaceserif::operators::backend::mfem{ template<VolumeEquationEquationT,typenameSchemaT> voidvalidate_volume_data(constVolumeData<EquationT, SchemaT>&data){ usingnamespaceutils::errors; constauto&discretization=data.configuration->discretization; validate_volume_discretization(discretization); for(constauto*column:data.bindings.columns){ column->validate(); } serif_ensure<SERiFErrorCode::CACHE_FE_BINDING_ERROR>( discretization.residual_binding.mesh().GetNodesSequence()==data.bindings.nodes_sequence, "Thepreparedequationmeshchanged;prepareagainorrebuilditsbindings."); } template<VolumeEquationEquationT,typenameSchemaT,typenameVisitT> voidfor_each_volume_element(constVolumeData<EquationT, SchemaT>&data,VisitT&&visit){ [&]<std::size_t...Indices>(std::index_sequence<Indices...>){ constautovisit_contribution=[&]<std::size_tIndex>(){ usingContribution=std::tuple_element_t<Index,equations::ContributionsOf<EquationT>>; for(constauto&element:data.contributions[Index]){ visit.templateoperator()<Contribution>(*element); } }; (visit_contribution.templateoperator()<Indices>(),...); }(std::make_index_sequence<std::tuple_size_v<equations::ContributionsOf<EquationT>>>{}); } template<VolumeEquationEquationT,typenameSchemaT,typenameVisitT> voidfor_each_volume_linearization(constVolumeData<EquationT, SchemaT>&data,VisitT&&visit){ [&]<std::size_t...Indices>(std::index_sequence<Indices...>){ constautovisit_contribution=[&]<std::size_tIndex>(){ usingContribution=std::tuple_element_t<Index,equations::ContributionsOf<EquationT>>; constauto&elements=data.contributions[Index]; for(std::size_telement=0;element<elements.size();++element){ visit.templateoperator()<Contribution>(*elements[element],*data.geometry_derivatives[Index][element]); } }; (visit_contribution.templateoperator()<Indices>(),...); }(std::make_index_sequence<std::tuple_size_v<equations::ContributionsOf<EquationT>>>{}); } template<VolumeEquationEquationT,typenameSchemaT> [[nodiscard]]intvolume_height(constVolumeData<EquationT, SchemaT>&data){ validate_volume_data(data); returndata.configuration->discretization.residual_binding.size(); } template<VolumeEquationEquationT,typenameSchemaT> [[nodiscard]]intvolume_width(constVolumeData<EquationT, SchemaT>&data){ validate_volume_data(data); intsize=data.configuration->discretization.displacement_binding.size(); for(constauto*column:data.bindings.columns){ size+=column->size(); } returnsize; } template<VolumeEquationEquationT,typenameSchemaT> [[nodiscard]]std::size_ttensor_element_count(constVolumeData<EquationT, SchemaT>&data){ validate_volume_data(data); std::size_tcount=0; for_each_volume_element(data,[&]<typenameContributionT>(constauto&element){ booltensor=element.basis->displacement_basis.uses_tensor()||element.geometry->test_basis.uses_tensor(); for(constauto&basis:element.geometry->operand_basis){ tensor=tensor||basis.uses_tensor(); } count+=tensor; }); returncount; } template<VolumeEquationEquationT,typenameSchemaT> [[nodiscard]]intintegration_order(constVolumeData<EquationT, SchemaT>&data,intelement){ usingnamespaceutils::errors; validate_volume_data(data); intorder=-1; for_each_volume_element(data,[&]<typenameContributionT>(constauto&entry){ if(entry.element==element){ order=std::max(order,entry.basis->rule->GetOrder()); } }); serif_ensure<SERiFErrorCode::OUT_OF_RANGE_ERROR,std::out_of_range>( order>=0,"Element{}isnotinthepreparedequationsupport.",element); returnorder; } template<VolumeEquationEquationT,typenameSchemaT> voidvolume_residual(constVolumeData<EquationT, SchemaT>&data,::mfem::Vector&result){ validate_volume_data(data); result=data.residual; } template<std::size_tCount> [[nodiscard]]std::array<discretization::backend::mfem::DofMap::Elements,Count>column_elements( conststd::array<constdiscretization::backend::mfem::DofMap*,Count>&columns ){ returnstd::apply([](constauto*...column){ returnstd::array<discretization::backend::mfem::DofMap::Elements,Count>{column->validated_elements()...}; },columns); } template<boolFields=true,boolGeometry=true,VolumeEquationEquationT,typenameSchemaT> voidapply_volume_direction( constVolumeData<EquationT, SchemaT>&data, conststd::array<const::mfem::Vector*,EquationPlan<EquationT>::column_count>&directions, const::mfem::Vector&displacement_direction, Workspace&workspace, ::mfem::Vector&result ){ usingPlan=EquationPlan<EquationT>; validate_volume_data(data); constauto&discretization=data.configuration->discretization; ifconstexpr(Geometry){ discretization.displacement_binding.validate_values(displacement_direction); } ifconstexpr(Fields){ for(std::size_tcolumn=0;column<Plan::column_count;++column){ data.bindings.columns[column]->validate_values(*directions[column]); } } workspace.full_result.SetSize(discretization.residual_binding.full_size()); workspace.full_result=0.0; workspace.operand_values.resize(Plan::operand_count); utils::CompensatedSumglobal_sum; constautorows=discretization.residual_binding.validated_elements(); constautocolumns=column_elements(data.bindings.columns); constautodisplacement=discretization.displacement_binding.validated_elements(); //Elementactionsareindependentandrunconcurrently,eachintoitsownresult.Theyarethenadded //intotheresidualonebyoneintheoriginalelementorder,soeverysumisunchanged. std::vector<std::function<void(Workspace&,::mfem::Vector&)>>actions; std::vector<int>action_elements; for_each_volume_linearization(data,[&]<typenameContributionT>(constauto&element,constauto&geometry_derivatives){ action_elements.push_back(element.element); actions.emplace_back([&](Workspace&local,::mfem::Vector&element_result){ element_jacobian_action<EquationT, ContributionT, Fields, Geometry>( element,geometry_derivatives,columns,directions,displacement, displacement_direction,local,element_result); }); }); std::vector<::mfem::Vector>element_results(actions.size()); constintworkers=utils::parallel::worker_count(); std::vector<Workspace>worker_workspaces(static_cast<std::size_t>(workers>1?workers-1:0)); for(auto&local:worker_workspaces){ local.operand_values.resize(Plan::operand_count); } utils::parallel::for_each_index(static_cast<int>(actions.size()),[&](constintindex,constintworker){ Workspace&local=worker==0?workspace:worker_workspaces[static_cast<std::size_t>(worker-1)]; actions[static_cast<std::size_t>(index)](local,element_results[static_cast<std::size_t>(index)]); }); for(std::size_tindex=0;index<actions.size();++index){ ifconstexpr(discretization::blocks::GlobalScalarTerm<typenamePlan::Test>){ global_sum.add(element_results[index][0]); }else{ rows.add_element(action_elements[index],element_results[index],workspace.full_result); } } ifconstexpr(discretization::blocks::GlobalScalarTerm<typenamePlan::Test>){ workspace.full_result[0]=global_sum.value(); } result=discretization.residual_binding.accumulate_dual(workspace.full_result); discretization.residual_binding.validate_values(result); } template<VolumeEquationEquationT,typenameSchemaT> voidapply_volume_displacements( constVolumeData<EquationT, SchemaT>&data, std::span<const::mfem::Vector*const>displacement_directions, std::span<::mfem::Vector*const>results ){ usingPlan=EquationPlan<EquationT>; validate_volume_data(data); constauto&discretization=data.configuration->discretization; for(constauto*direction:displacement_directions){ discretization.displacement_binding.validate_values(*direction); } constautorows=discretization.residual_binding.validated_elements(); constautocolumns=column_elements(data.bindings.columns); constautodisplacement=discretization.displacement_binding.validated_elements(); std::vector<std::function<void(Workspace&,std::span<::mfem::Vector*const>)>>actions; std::vector<int>action_elements; for_each_volume_linearization(data,[&]<typenameContributionT>(constauto&element,constauto&geometry_derivatives){ action_elements.push_back(element.element); actions.emplace_back([&](Workspace&local,std::span<::mfem::Vector*const>element_results){ element_geometry_actions<EquationT, ContributionT>( element,geometry_derivatives,displacement,displacement_directions,local,element_results); }); }); conststd::size_telements=actions.size(); conststd::size_tcount=displacement_directions.size(); //element_results[direction*elements+element] std::vector<::mfem::Vector>element_results(elements*count); constintworkers=utils::parallel::worker_count(); std::vector<Workspace>worker_workspaces(static_cast<std::size_t>(workers)); for(auto&local:worker_workspaces){ local.operand_values.resize(Plan::operand_count); } utils::parallel::for_each_index(static_cast<int>(elements),[&](constintelement,constintworker){ std::vector<::mfem::Vector*>lanes(count); for(std::size_tdirection=0;direction<count;++direction){ lanes[direction]=&element_results[direction*elements+static_cast<std::size_t>(element)]; } actions[static_cast<std::size_t>(element)](worker_workspaces[static_cast<std::size_t>(worker)],lanes); }); utils::parallel::for_each_index(static_cast<int>(count),[&](constintdirection,int){ constautofirst=static_cast<std::size_t>(direction)*elements; ::mfem::Vectorfull(discretization.residual_binding.full_size()); full=0.0; utils::CompensatedSumglobal_sum; for(std::size_tindex=0;index<elements;++index){ ifconstexpr(discretization::blocks::GlobalScalarTerm<typenamePlan::Test>){ global_sum.add(element_results[first+index][0]); }else{ rows.add_element(action_elements[index],element_results[first+index],full); } } ifconstexpr(discretization::blocks::GlobalScalarTerm<typenamePlan::Test>){ full[0]=global_sum.value(); } auto&result=*results[static_cast<std::size_t>(direction)]; result=discretization.residual_binding.accumulate_dual(full); discretization.residual_binding.validate_values(result); }); } template<VolumeEquationEquationT,typenameSchemaT,typenameCoefficientsT> voidapply_jacobian( constVolumeData<EquationT, SchemaT>&data, constCoefficientsT&direction, const::mfem::Vector&displacement_direction, Workspace&workspace, ::mfem::Vector&result ){ usingnamespaceutils::errors; usingPlan=EquationPlan<EquationT>; std::array<const::mfem::Vector*,Plan::column_count>directions; Plan::for_each_column([&]<typenameOperandT>(OperandT,std::size_tcolumn){ constauto&input=direction.templateget<typenameOperandT::term_type>(); serif_ensure<SERiFErrorCode::CACHE_FE_BINDING_ERROR,std::invalid_argument>( &input.binding==data.bindings.columns[column], "Jacobiandirectionsmustusethepreparedcoefficientbindings."); directions[column]=&input.values; }); apply_volume_direction(data,directions,displacement_direction,workspace,result); } template<VolumeEquationEquationT,typenameSchemaT> voidapply_jacobian( constVolumeData<EquationT, SchemaT>&data, const::mfem::Vector&direction, Workspace&workspace, ::mfem::Vector&result ){ usingnamespaceutils::errors; usingPlan=EquationPlan<EquationT>; serif_ensure<SERiFErrorCode::INVALID_DOF_COUNT,std::invalid_argument>( direction.Size()==volume_width(data),"Packedequationdirectionhasthewrongsize."); std::array<::mfem::Vector,Plan::column_count>values; std::array<const::mfem::Vector*,Plan::column_count>directions; intoffset=0; for(std::size_tcolumn=0;column<Plan::column_count;++column){ values[column].SetSize(data.bindings.columns[column]->size()); std::copy_n(direction.GetData()+offset,values[column].Size(),values[column].GetData()); directions[column]=&values[column]; offset+=values[column].Size(); } ::mfem::Vectordisplacement_direction(data.configuration->discretization.displacement_binding.size()); std::copy_n(direction.GetData()+offset,displacement_direction.Size(),displacement_direction.GetData()); apply_volume_direction(data,directions,displacement_direction,workspace,result); } template<VolumeEquationEquationT,typenameSchemaT> voidapply_jacobian_transpose( constVolumeData<EquationT, SchemaT>&data, const::mfem::Vector&residual_dual, Workspace&workspace, ::mfem::Vector&result ){ usingPlan=EquationPlan<EquationT>; validate_volume_data(data); constauto&discretization=data.configuration->discretization; discretization.residual_binding.validate_values(residual_dual); std::array<::mfem::Vector,Plan::column_count>full_columns; std::array<::mfem::Vector,Plan::column_count>element_columns; std::array<utils::CompensatedSum,Plan::column_count>scalar_sums; for(std::size_tcolumn=0;column<Plan::column_count;++column){ full_columns[column].SetSize(data.bindings.columns[column]->full_size()); full_columns[column]=0.0; } ::mfem::Vectorfull_displacement(discretization.displacement_binding.full_size()); ::mfem::Vectorelement_displacement; full_displacement=0.0; constautorow_elements=discretization.residual_binding.validated_elements(); constautocolumns=column_elements(data.bindings.columns); constautodisplacement_elements=discretization.displacement_binding.validated_elements(); for_each_volume_linearization(data,[&]<typenameContributionT>(constauto&element,constauto&geometry_derivatives){ element_jacobian_transpose<EquationT, ContributionT>( element,geometry_derivatives,row_elements,residual_dual,workspace, element_columns,element_displacement); Plan::for_each_column([&]<typenameOperandT>(OperandT,std::size_tcolumn){ if(element_columns[column].Size()==0){ return; } ifconstexpr(discretization::blocks::GlobalScalarTerm<typenameOperandT::term_type>){ scalar_sums[column].add(element_columns[column][0]); }else{ columns[column].add_element(element.element,element_columns[column],full_columns[column]); } }); displacement_elements.add_element(element.element,element_displacement,full_displacement); }); ::mfem::Vectorpacked(volume_width(data)); intoffset=0; Plan::for_each_column([&]<typenameOperandT>(OperandT,std::size_tcolumn){ ifconstexpr(discretization::blocks::GlobalScalarTerm<typenameOperandT::term_type>){ full_columns[column][0]=scalar_sums[column].value(); } constautovalues=data.bindings.columns[column]->accumulate_dual(full_columns[column]); data.bindings.columns[column]->validate_values(values); std::copy_n(values.GetData(),values.Size(),packed.GetData()+offset); offset+=values.Size(); }); constautodisplacement=discretization.displacement_binding.accumulate_dual(full_displacement); discretization.displacement_binding.validate_values(displacement); std::copy_n(displacement.GetData(),displacement.Size(),packed.GetData()+offset); result=packed; } }//namespaceserif::operators::backend::mfem