dof_map.hpp cmath optional memory serif/backend/mfem/discretization/reconstruction.hpp stdexcept mfem/fem/fespace.hpp mfem/fem/pfespace.hpp serif/utils/error/errors.hpp serif/utils/error/nonfinite.hpp src/include/serif/backend/mfem/discretization/dof_selection.hpp src/include/serif/backend/mfem/discretization/element_coordinates.hpp src/include/serif/backend/mfem/discretization/term_binding.hpp src/include/serif/backend/mfem/geometry/deformation/displacement_mapping.hpp src/include/serif/backend/mfem/model/surface_potential.hpp src/include/serif/backend/mfem/operators/operand_evaluation.hpp src/include/serif/backend/mfem/serialize/resources.hpp src/include/serif/backend/mfem/serialize/top.hpp serif::discretization::backend::mfem::DofMap serif::discretization::backend::mfem::DofMap::Elements serif serif::discretization serif::discretization::backend serif::discretization::backend::mfem This file contains the DofMap which allows for the full dof vector to be projected into a reduced space of dofs on supported elements for some field Generally, while much of the code here may be exercised, you will rarely see a DofMap be directly invoked. Rather, these are generally implicitly constructed through their child class term_binding. Emily M. Boudreaux #pragmaonce #include<cmath> #include<optional> #include<memory> #include"serif/backend/mfem/discretization/reconstruction.hpp" #include<stdexcept> #include"mfem/fem/fespace.hpp" #include"mfem/fem/pfespace.hpp" #include"serif/utils/error/errors.hpp" #include"serif/utils/error/nonfinite.hpp" namespaceserif::discretization::backend::mfem{ classElementCoordinates; classDofMap{ protected: DofMap( ::mfem::FiniteElementSpace&space, const::mfem::Array<int>&element_support, std::shared_ptr<constReconstructionPlan>reconstruction={} ): m_space(&space), m_mesh(*space.GetMesh()), m_space_sequence(space.GetSequence()), m_mesh_sequence(space.GetMesh()->GetSequence()), m_attributes(space.GetNE()), m_element_support(element_support), m_full_to_reduced(space.GetVSize()), m_reconstruction(std::move(reconstruction)){ usingnamespaceutils::errors; #ifdefMFEM_USE_MPI serif_ensure<SERiFErrorCode::INVALID_ARGUMENT,std::invalid_argument>(dynamic_cast<::mfem::ParFiniteElementSpace*>(&space)==nullptr,"DofMapdoesnotyetassembleparalleltrueDOFs."); #endif m_prolongation=space.GetConformingProlongation(); serif_ensure<SERiFErrorCode::INVALID_ARGUMENT,std::invalid_argument>(!constrained()||!space.IsVariableOrder(),"Constrainedtermbindingsrequirefixed-orderserialspaces;varyingelementorderisnotsupported."); serif_ensure<SERiFErrorCode::INVALID_DIMENSION,std::invalid_argument>(space.GetMesh()->Dimension()==3&&space.GetMesh()->SpaceDimension()==3,"Termbindingscurrentlyrequireathree-dimensionalmesh."); serif_ensure<SERiFErrorCode::INVALID_ARGUMENT,std::invalid_argument>(element_support.Size()==space.GetNE(),"Elementsupportdoesnotmatchthespace."); m_full_to_reduced=-1; ::mfem::Array<int>dofs; for(intelement=0;element<space.GetNE();++element){ m_attributes[element]=space.GetMesh()->GetAttribute(element); if(element_support[element]==0){ continue; } space.GetElementVDofs(element,dofs); for(constintdof:dofs){ m_full_to_reduced[dof<0?-1-dof:dof]=0; } } if(m_reconstruction){ m_boundary_attributes.emplace(m_mesh.GetNBE()); for(intboundary=0;boundary<m_mesh.GetNBE();++boundary){ (*m_boundary_attributes)[boundary]=m_mesh.GetBdrAttribute(boundary); } } if(m_reconstruction){m_reconstruction->validate_binding(*this);} if(constrained()){ build_constrained_map(); return; } for(intdof=0;dof<space.GetVSize();++dof){ if(m_full_to_reduced[dof]==0){ m_full_to_reduced[dof]=m_reduced_to_full.Size(); m_reduced_to_full.Append(dof); } } } DofMap( ::mfem::Mesh&mesh, const::mfem::Array<int>&element_support ): m_space(nullptr), m_mesh(mesh), m_space_sequence(-1), m_mesh_sequence(mesh.GetSequence()), m_attributes(mesh.GetNE()), m_element_support(element_support), m_reduced_to_full(1), m_full_to_reduced(1){ usingnamespaceutils::errors; serif_ensure<SERiFErrorCode::INVALID_DIMENSION,std::invalid_argument>(mesh.Dimension()==3&&mesh.SpaceDimension()==3,"Termbindingscurrentlyrequireathree-dimensionalmesh."); serif_ensure<SERiFErrorCode::INVALID_ARGUMENT,std::invalid_argument>(element_support.Size()==mesh.GetNE(),"Elementsupportdoesnotmatchthemesh."); m_reduced_to_full[0]=m_full_to_reduced[0]=0; for(intelement=0;element<mesh.GetNE();++element){ m_attributes[element]=mesh.GetAttribute(element); } } ~DofMap()=default; public: classElementsfinal{ public: voidelement_values(intelement,const::mfem::Vector&reduced,::mfem::Vector&values)const; voidadd_element(intelement,::mfem::Vector&values,::mfem::Vector&full)const; [[nodiscard]]ElementCoordinatescoordinates(intelement)const; private: friendclassDofMap; friendclassElementCoordinates; explicitElements(constDofMap&binding):m_binding(binding){} constDofMap&m_binding; }; [[nodiscard]]Elementsvalidated_elements()const{ validate(); returnElements(*this); } [[nodiscard]]conststd::shared_ptr<constReconstructionPlan>&reconstruction()constnoexcept{returnm_reconstruction;} [[nodiscard]]constElementReconstruction*reconstruction_element(intelement)const{ if(m_reconstruction==nullptr){returnnullptr;} returnm_reconstruction->element(element); } DofMap(constDofMap&)=delete; DofMap&operator=(constDofMap&)=delete; DofMap(DofMap&&)=delete; DofMap&operator=(DofMap&&)=delete; [[nodiscard]]::mfem::FiniteElementSpace&space()const{ usingnamespaceutils::errors; serif_ensure<SERiFErrorCode::CACHE_FE_BINDING_ERROR,std::logic_error>(m_space!=nullptr,"Globalscalarsdonothaveafinite-elementspace."); return*m_space; } [[nodiscard]]::mfem::Mesh&mesh()constnoexcept{returnm_mesh;} [[nodiscard]]intfull_size()constnoexcept{returnm_full_to_reduced.Size();} [[nodiscard]]intsize()constnoexcept{ returnm_reduced_to_full.Size(); } [[nodiscard]]std::optional<int>reduced_dof(constintfull_dof)const{ usingnamespaceutils::errors; serif_ensure<SERiFErrorCode::INVALID_DOF_COUNT,std::out_of_range>(full_dof>=0&&full_dof<full_size(),"FullDOFindexisoutofrange."); constintreduced=m_full_to_reduced[full_dof]; returnreduced<0?std::nullopt:std::optional<int>{reduced}; } [[nodiscard]]intfull_dof(constintreduced)const{ usingnamespaceutils::errors; validate_structure(); serif_ensure<SERiFErrorCode::INVALID_DOF_COUNT,std::out_of_range>(reduced>=0&&reduced<size(), "SupportedDOFindex{}isoutsidethebindingofsize{}.",reduced,size()); returnm_reduced_to_full[reduced]; } [[nodiscard]]boolconstrained()constnoexcept{returnm_prolongation!=nullptr;} [[nodiscard]]inttrue_size()constnoexcept{returnm_space==nullptr?1:m_space->GetTrueVSize();} template<typenameVisitor> voidfor_each_coefficient(constintlocal_dof,Visitor&&visit)const{ usingnamespaceutils::errors; validate_structure(); serif_ensure<SERiFErrorCode::INVALID_DOF_COUNT,std::out_of_range>(local_dof>=0&&local_dof<full_size(),"LocalDOFindexisoutsideitsfinite-elementspace."); if(!constrained()){ constintindex=m_full_to_reduced[local_dof]; if(index>=0){ visit(index,1.0); } return; } constint*offsets=m_prolongation->GetI(); constint*columns=m_prolongation->GetJ(); constdouble*weights=m_prolongation->GetData(); for(intentry=offsets[local_dof];entry<offsets[local_dof+1];++entry){ constintindex=m_true_to_reduced[columns[entry]]; if(index>=0&&weights[entry]!=0.0){ visit(index,weights[entry]); } } } [[nodiscard]]::mfem::Vectorgather_true(const::mfem::Vector&values)const{ usingnamespaceutils::errors; validate(); serif_ensure<SERiFErrorCode::INVALID_DOF_COUNT,std::invalid_argument>(values.Size()==true_size(), "Independentcoefficientvectorhassize{},expected{}.",values.Size(),true_size()); if(!constrained()){ returngather(values); } ::mfem::Vectorresult(size()); for(intindex=0;index<size();++index){ result[index]=values[m_reduced_to_true[index]]; } returnresult; } [[nodiscard]]::mfem::Vectorscatter_true(const::mfem::Vector&values)const{ validate_values(values); if(!constrained()){ returnscatter(values); } ::mfem::Vectorresult(true_size()); result=0.0; for(intindex=0;index<size();++index){ result[m_reduced_to_true[index]]=values[index]; } returnresult; } [[nodiscard]]::mfem::Vectoraccumulate_dual(const::mfem::Vector&full)const{ ::mfem::Vectorresult; accumulate_dual(full,result); returnresult; } voidaccumulate_dual(const::mfem::Vector&full,::mfem::Vector&result)const{ usingnamespaceutils::errors; validate(); serif_ensure<SERiFErrorCode::INVALID_DOF_COUNT,std::invalid_argument>(full.Size()==full_size(), "Localdualvectorhassize{},expected{}.",full.Size(),full_size()); if(!constrained()){ gather(full,result); return; } ::mfem::Vectorindependent(true_size()); m_prolongation->MultTranspose(full,independent); result=gather_true(independent); } [[nodiscard]]boolsupports(constintelement)const{ usingnamespaceutils::errors; serif_ensure<SERiFErrorCode::INVALID_ELEMENT_ID_ERROR,std::out_of_range>(element>=0&&element<m_element_support.Size(),"Elementindexisoutofrange."); returnm_element_support[element]!=0; } voidvalidate()const{ usingnamespaceutils::errors; validate_structure(); if(m_boundary_attributes){ serif_ensure<SERiFErrorCode::CACHE_FE_BINDING_ERROR>(m_mesh.GetNBE()==m_boundary_attributes->Size(),"Rebuildreconstructedbindingsafterboundarychanges."); for(intboundary=0;boundary<m_mesh.GetNBE();++boundary){ serif_ensure<SERiFErrorCode::CACHE_FE_BINDING_ERROR>(m_mesh.GetBdrAttribute(boundary)==(*m_boundary_attributes)[boundary], "Rebuildreconstructedbindingsafterboundarylabelschange."); } } for(intelement=0;element<m_mesh.GetNE();++element){ serif_ensure<SERiFErrorCode::CACHE_FE_BINDING_ERROR>(m_mesh.GetAttribute(element)==m_attributes[element],"Termbindingdomainattributeschangedunexpectedly."); } } voidvalidate_values(const::mfem::Vector&values)const{ usingnamespaceutils::errors; validate(); serif_ensure<SERiFErrorCode::INVALID_DOF_COUNT,std::invalid_argument>(values.Size()==size(),"Termbindingsizeis{},expected{}supportedDOFs.",values.Size(),size()); for(inti=0;i<values.Size();++i){ serif_ensure<SERiFErrorCode::NON_FINITE_VALUE_ERROR,NonFiniteValue>(std::isfinite(values[i]),"Termbindingcontainsanon-finitecoefficient."); } } [[nodiscard]]::mfem::Vectorgather(const::mfem::Vector&full)const{ ::mfem::Vectorresult; gather(full,result); returnresult; } voidgather(const::mfem::Vector&full,::mfem::Vector&result)const{ usingnamespaceutils::errors; validate(); serif_ensure<SERiFErrorCode::INVALID_DOF_COUNT,std::invalid_argument>(full.Size()==full_size(),"Fullcoefficientvectordoesnotmatchitsspace."); if(full.GetData()==result.GetData()){ const::mfem::Vectorcopy(full); gather(copy,result); return; } if(constrained()){ ::mfem::Vectorindependent(true_size()); m_space->GetConformingRestriction()->Mult(full,independent); result=gather_true(independent); return; } result.SetSize(size()); for(intindex=0;index<size();++index){ result[index]=full[m_reduced_to_full[index]]; } } [[nodiscard]]::mfem::Vectorscatter(const::mfem::Vector&reduced)const{ usingnamespaceutils::errors; validate_values(reduced); ::mfem::Vectorresult(full_size()); if(constrained()){ m_prolongation->Mult(scatter_true(reduced),result); returnresult; } result=0.0; for(inti=0;i<size();++i){ result[m_reduced_to_full[i]]=reduced[i]; } returnresult; } voidelement_values(intelement,const::mfem::Vector&reduced,::mfem::Vector&values)const; voidadd_element(intelement,::mfem::Vector&values,::mfem::Vector&full)const; private: friendclassElementCoordinates; voidbuild_constrained_map(){ usingnamespaceutils::errors; m_true_to_reduced.SetSize(true_size()); m_true_to_reduced=-1; constint*offsets=m_prolongation->GetI(); constint*columns=m_prolongation->GetJ(); constdouble*weights=m_prolongation->GetData(); for(intlocal=0;local<full_size();++local){ if(m_full_to_reduced[local]<0){ continue; } for(intentry=offsets[local];entry<offsets[local+1];++entry){ if(weights[entry]!=0.0){ m_true_to_reduced[columns[entry]]=0; } } } const::mfem::SparseMatrix*restriction=m_space->GetConformingRestriction(); serif_ensure<SERiFErrorCode::INVALID_ARGUMENT,std::invalid_argument>(restriction!=nullptr, "Aconstrainedserialspacemustprovideitscoefficientrestriction."); for(intindependent=0;independent<true_size();++independent){ if(m_true_to_reduced[independent]<0){ continue; } constintbegin=restriction->GetI()[independent]; constintend=restriction->GetI()[independent+1]; serif_ensure<SERiFErrorCode::INVALID_ARGUMENT,std::invalid_argument>(end==begin+1&&restriction->GetData()[begin]==1.0, "Fixed-ordercoefficientrestrictionmustselectoneindependentlocalDOF;coordinate{}hasanunsupportedrestriction.",independent); m_true_to_reduced[independent]=m_reduced_to_true.Size(); m_reduced_to_true.Append(independent); m_reduced_to_full.Append(restriction->GetJ()[begin]); } m_full_to_reduced=-1; for(intlocal=0;local<full_size();++local){ constintbegin=offsets[local]; if(offsets[local+1]==begin+1&&weights[begin]==1.0){ m_full_to_reduced[local]=m_true_to_reduced[columns[begin]]; } } } voidvalidate_structure()const{ usingnamespaceutils::errors; serif_ensure<SERiFErrorCode::CACHE_FE_BINDING_ERROR,std::logic_error>(m_mesh.GetSequence()==m_mesh_sequence&&m_mesh.GetNE()==m_attributes.Size()&&(m_space==nullptr||(m_space->GetMesh()==&m_mesh&&m_space->GetSequence()==m_space_sequence&&m_space->GetVSize()==full_size())),"Rebuildtermbindingsafterdiscretizationupdates."); } ::mfem::FiniteElementSpace*m_space; ::mfem::Mesh&m_mesh; longm_space_sequence; longm_mesh_sequence; ::mfem::Array<int>m_attributes; ::mfem::Array<int>m_element_support; const::mfem::SparseMatrix*m_prolongation{nullptr}; ::mfem::Array<int>m_true_to_reduced; ::mfem::Array<int>m_reduced_to_true; ::mfem::Array<int>m_reduced_to_full; ::mfem::Array<int>m_full_to_reduced; std::optional<::mfem::Array<int>>m_boundary_attributes; std::shared_ptr<constReconstructionPlan>m_reconstruction; }; }//namespaceserif::discretization::backend::mfem