boundary_trace.cpp serif/backend/mfem/discretization/boundary_trace.hpp algorithm cmath limits map vector serif serif::discretization serif::discretization::backend serif::discretization::backend::mfem #include"serif/backend/mfem/discretization/boundary_trace.hpp" #include<algorithm> #include<cmath> #include<limits> #include<map> #include<vector> namespaceserif::discretization::backend::mfem { namespace { usingStencil=std::map<int,double>; voidvalidate_trace_space(const::mfem::FiniteElementSpace&space) { usingnamespaceutils::errors; serif_ensure<SERiFErrorCode::INVALID_ARGUMENT,std::invalid_argument>(space.GetVDim()==1&&space.FEColl()->GetContType()==::mfem::FiniteElementCollection::CONTINUOUS,"BoundaryinterpolationrequiresascalarH1space."); ::mfem::Array<int>dofs; for(intelement=0;element<space.GetNE();++element) { constauto*finite_element=dynamic_cast<const::mfem::NodalFiniteElement*>(space.GetFE(element)); serif_ensure<SERiFErrorCode::INVALID_ARGUMENT,std::invalid_argument>(finite_element!=nullptr&&finite_element->GetRangeType()==::mfem::FiniteElement::SCALAR&&finite_element->GetMapType()==::mfem::FiniteElement::VALUE,"BoundaryinterpolationrequiresnodalscalarVALUEelements;Bernsteincoefficientsarenotnodalvalues."); constauto*transformation=space.GetElementDofs(element,dofs); serif_ensure<SERiFErrorCode::INVALID_ARGUMENT,std::invalid_argument>(transformation==nullptr||transformation->IsIdentity(),"BoundaryinterpolationdoesnotsupportnonidentityscalarDOFtransformations."); serif_ensure<SERiFErrorCode::INVALID_DOF_COUNT,std::invalid_argument>(finite_element->GetNodes().GetNPoints()==dofs.Size(),"Boundaryinterpolationrequiresonecanonicalnodeperelementcoefficient."); } } [[nodiscard]]Stencilsample_stencil(constDofMap&input,const::mfem::FiniteElementSpace*space,constintelement,const::mfem::IntegrationPoint&point) { usingnamespaceutils::errors; if(space==nullptr) { return{{0,1.0}}; } ::mfem::Array<int>dofs; space->GetElementDofs(element,dofs); ::mfem::Vectorshape(dofs.Size()); space->GetFE(element)->CalcShape(point,shape); Stencilstencil; for(intlocal=0;local<dofs.Size();++local) { constintencoded=dofs[local]; serif_ensure<SERiFErrorCode::NON_FINITE_VALUE_ERROR,std::invalid_argument>(std::isfinite(shape[local]),"Boundaryinterpolationproducedanon-finitebasisvalue."); boolsupported=false; input.for_each_coefficient(::mfem::FiniteElementSpace::DecodeDof(encoded),[&](constintindex,constdoubleweight){ supported=true; stencil[index]+=weight*(encoded<0?-shape[local]:shape[local]); serif_ensure<SERiFErrorCode::NON_FINITE_VALUE_ERROR,std::invalid_argument>(std::isfinite(stencil[index]),"Boundaryinterpolationoverflowedwhilecombiningrepeatedinputcoefficients."); }); serif_ensure<SERiFErrorCode::INVALID_DOMAIN_ERROR,std::invalid_argument>(supported,"Boundaryinterpolationencounteredaninputcoefficientoutsideitsdeclaredelementsupport."); } returnstencil; } [[nodiscard]]boolequivalent_stencils(constStencil&first,constStencil&second) { std::map<int,longdouble>difference(first.begin(),first.end()); longdoublemagnitude=1.0L; for(constauto&[column,value]:second) { difference[column]-=value; magnitude+=std::abs(static_cast<longdouble>(value)); } constlongdoubletolerance=128.0L*std::numeric_limits<double>::epsilon()*magnitude; returnstd::ranges::all_of(difference,[tolerance](constauto&entry) { returnstd::abs(entry.second)<=tolerance; }); } } ScalarBoundaryTrace::ScalarBoundaryTrace(std::shared_ptr<constDofSelection>rows,constDofMap&input,std::shared_ptr<const::mfem::SparseMatrix>interpolation):m_rows(std::move(rows)),m_binding(input),m_interpolation(std::move(interpolation)) { } constDofMap&ScalarBoundaryTrace::binding()constnoexcept { returnm_binding; } constDofSelection&ScalarBoundaryTrace::rows()constnoexcept { return*m_rows; } const::mfem::SparseMatrix&ScalarBoundaryTrace::interpolation()const { validate(); return*m_interpolation; } voidScalarBoundaryTrace::validate()const { m_rows->validate(); m_binding.validate(); } ScalarBoundaryTracedetail::build_boundary_trace(std::shared_ptr<constDofSelection>rows,constDofMap&input,const::mfem::FiniteElementSpace*input_space) { usingnamespaceutils::errors; serif_ensure<SERiFErrorCode::INVALID_ARGUMENT,std::invalid_argument>(rows!=nullptr&&rows->size()>0,"Boundaryinterpolationrequiresanonemptyresidual-nodeselection."); rows->validate(); input.validate(); constDofMap&row_binding=rows->binding(); serif_ensure<SERiFErrorCode::MISMATCHED_MESHING_MAPS,std::invalid_argument>(&row_binding.mesh()==&input.mesh(),"Boundaryinterpolationrequiresinputandresidualbindingsonthesamemesh."); const::mfem::FiniteElementSpace&row_space=row_binding.space(); validate_trace_space(row_space); if(input_space!=nullptr) { validate_trace_space(*input_space); } std::vector<int>selected(static_cast<std::size_t>(row_binding.size()),-1); std::vector<Stencil>stencils(static_cast<std::size_t>(rows->size())); std::vector<bool>sampled(static_cast<std::size_t>(rows->size()),false); for(introw=0;row<rows->size();++row) { selected[rows->reduced_dofs()[row]]=row; } ::mfem::Array<int>dofs; for(intelement=0;element<row_space.GetNE();++element) { if(!row_binding.supports(element)||!input.supports(element)) { continue; } row_space.GetElementDofs(element,dofs); const::mfem::IntegrationRule&nodes=row_space.GetFE(element)->GetNodes(); for(intlocal=0;local<dofs.Size();++local) { serif_ensure<SERiFErrorCode::INVALID_ARGUMENT,std::invalid_argument>(dofs[local]>=0,"NodalresidualcoordinatesrequireunsignedscalarH1DOFs."); constautoreduced=row_binding.reduced_dof(dofs[local]); constintrow=reduced?selected[*reduced]:-1; if(row<0) { continue; } Stencilcandidate=sample_stencil(input,input_space,element,nodes.IntPoint(local)); serif_ensure<SERiFErrorCode::INVALID_ARGUMENT,std::invalid_argument>(!sampled[row]||equivalent_stencils(stencils[row],candidate),"InputH1interpolationisinconsistentbetweenincidentelementsatselectedresidualrow{};verifycompatibleconstrainednodalspaces.",row); if(!sampled[row]) { stencils[row]=std::move(candidate); sampled[row]=true; } } } autointerpolation=std::make_shared<::mfem::SparseMatrix>(rows->size(),input.size()); for(introw=0;row<rows->size();++row) { serif_ensure<SERiFErrorCode::INVALID_DOMAIN_ERROR,std::invalid_argument>(sampled[row],"Selectedresidualnode{}hasnoincidentelementinboththeresidualandinputsupports.",row); for(constauto&[column,weight]:stencils[row]) { interpolation->Add(row,column,weight); } } interpolation->Finalize(); returnScalarBoundaryTrace(std::move(rows),input,std::move(interpolation)); } }