radial_interpolation.cpp algorithm cmath limits mfem/fem/fespace.hpp serif/geometry/deformation/backend/mfem/radial_interpolation.hpp serif/geometry/deformation/backend/mfem/detail/radial_locator.hpp serif serif::geometry serif::geometry::deformation serif::geometry::deformation::backend serif::geometry::deformation::backend::mfem #include<algorithm> #include<cmath> #include<limits> #include"mfem/fem/fespace.hpp" #ifdefMFEM_USE_MPI #include"mfem/fem/pfespace.hpp" #endif #include"serif/geometry/deformation/backend/mfem/radial_interpolation.hpp" #include"serif/geometry/deformation/backend/mfem/detail/radial_locator.hpp" namespaceserif::geometry::deformation::backend::mfem { namespace { [[nodiscard]]DeformationStatusvalidate_pairing(const::mfem::FiniteElementSpace&scalar_space,const::mfem::FiniteElementSpace&vector_space,const::mfem::Mesh&logical_mesh) { #ifdefMFEM_USE_MPI if(dynamic_cast<const::mfem::ParFiniteElementSpace*>(&vector_space)!=nullptr) { returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::incompatible_discretization,.message="RadialinterpolationdoesnotyetsupportdistributedvectorDOFs."}); } #endif const::mfem::Mesh&physical_mesh=*scalar_space.GetMesh(); if(vector_space.GetMesh()!=&physical_mesh||vector_space.FEColl()!=scalar_space.FEColl()||vector_space.GetVDim()!=3||vector_space.GetOrdering()!=::mfem::Ordering::byNODES||vector_space.GetTrueVSize()!=3*scalar_space.GetNDofs()||!vector_space.Conforming()) { returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::incompatible_discretization,.message="Radialinterpolationrequiresacomponent-blockvectorspaceanditsscalarcompanionononemesh."}); } if(constDeformationStatusnumbering=validate_companion_numbering(scalar_space,vector_space);!numbering) { returnnumbering; } if(logical_mesh.Dimension()!=3||logical_mesh.SpaceDimension()!=3||logical_mesh.Nonconforming()||physical_mesh.GetNBE()!=logical_mesh.GetNBE()||physical_mesh.GetNE()!=logical_mesh.GetNE()||physical_mesh.GetNV()!=logical_mesh.GetNV()) { returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::incompatible_discretization,.message="Physicalandlogicalreferencemeshesmusthavepairedconformingtopology."}); } ::mfem::Array<int>physical_vertices,logical_vertices; for(intboundary=0;boundary<physical_mesh.GetNBE();++boundary) { physical_mesh.GetBdrElementVertices(boundary,physical_vertices); logical_mesh.GetBdrElementVertices(boundary,logical_vertices); if(physical_mesh.GetBdrAttribute(boundary)!=logical_mesh.GetBdrAttribute(boundary)||physical_mesh.GetBdrElementGeometry(boundary)!=logical_mesh.GetBdrElementGeometry(boundary)||physical_vertices.Size()!=logical_vertices.Size()||!std::equal(physical_vertices.begin(),physical_vertices.end(),logical_vertices.begin())) { returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::incompatible_discretization,.message="Physicalandlogicalboundaryelementsmusthaveidenticalattributesandvertexordering.",.element=boundary}); } } return{}; } [[nodiscard]]std::expected<std::vector<int>,DeformationFailure>node_support(const::mfem::FiniteElementSpace&scalar_space,std::span<constint>elements) { if(elements.size()!=static_cast<std::size_t>(scalar_space.GetNE())) { returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::invalid_size,.message="Aradialextensionsupportmaskmustcontainoneentrypermeshelement."}); } std::vector<int>result(scalar_space.GetNDofs(),0); ::mfem::Array<int>dofs; for(intelement=0;element<scalar_space.GetNE();++element) { if(elements[element]!=0&&elements[element]!=1) { returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::invalid_configuration,.message="Radialelementsupportsmustcontainonlyzeroandone.",.element=element}); } if(elements[element]==0) { continue; } scalar_space.GetElementDofs(element,dofs); for(constintdof:dofs) { result[::mfem::FiniteElementSpace::DecodeDof(dof)]=1; } } returnresult; } [[nodiscard]]DeformationStatusappend_row(const::mfem::FiniteElementSpace&scalar_space,constdetail::LocatedBoundaryPoint&location,std::span<constint>surface_indices,RadialInterpolationData&result) { ::mfem::Array<int>dofs; scalar_space.GetBdrElementDofs(location.boundary_element,dofs); const::mfem::FiniteElement*element=scalar_space.GetBE(location.boundary_element); if(element==nullptr||element->GetDof()!=dofs.Size()) { returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::incompatible_discretization,.message="ThesurfacetracebasisdoesnotmatchtheboundaryDOFs.",.element=location.boundary_element}); } ::mfem::Vectorshape(dofs.Size()); element->CalcShape(location.point,shape); doublepartition=0.0; for(intnode=0;node<dofs.Size();++node) { if(!std::isfinite(shape[node])) { returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::non_finite,.message="Asurfaceinterpolationweightisnon-finite.",.element=location.boundary_element}); } partition+=shape[node]; if(std::abs(shape[node])<=64.0*std::numeric_limits<double>::epsilon()) { continue; } constintsurface_index=surface_indices[::mfem::FiniteElementSpace::DecodeDof(dofs[node])]; if(surface_index<0) { returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::unresolved_surface,.message="AsurfaceinterpolationDOFisabsentfromthecanonicalsurfaceselection."}); } result.surface_columns.push_back(surface_index); result.interpolation_weights.push_back(shape[node]); } if(std::abs(partition-1.0)>2.0e-12) { returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::invalid_geometry,.message="Aradialinterpolationrowfailstopreserveconstants."}); } return{}; } }//namespace std::expected<RadialInterpolation,DeformationFailure>detail::build_radial_interpolation( const::mfem::FiniteElementSpace&scalar_space, const::mfem::FiniteElementSpace&vector_space, ::mfem::Mesh&logical_mesh, std::span<constint>stellar_element_support, std::span<constint>vacuum_element_support, intsurface_attribute, intinfinity_attribute, conststd::vector<int>&selected) { constDeformationStatuspairing=validate_pairing(scalar_space,vector_space,logical_mesh); if(!pairing) { returnstd::unexpected(pairing.error()); } constautostellar_support=node_support(scalar_space,stellar_element_support); constautovacuum_support=node_support(scalar_space,vacuum_element_support); constautopositions=detail::sample_reference_positions(scalar_space,logical_mesh); constautostellar_radius=detail::boundary_logical_radius(logical_mesh,surface_attribute); constautoinfinity_radius=detail::boundary_logical_radius(logical_mesh,infinity_attribute); if(!stellar_support) { returnstd::unexpected(stellar_support.error()); } if(!vacuum_support) { returnstd::unexpected(vacuum_support.error()); } if(!positions) { returnstd::unexpected(positions.error()); } if(!stellar_radius) { returnstd::unexpected(stellar_radius.error()); } if(!infinity_radius) { returnstd::unexpected(infinity_radius.error()); } if(!(*infinity_radius>*stellar_radius)) { returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::invalid_geometry,.message="Logicalinfinitymustlieoutsidethelogicalstellarsurface."}); } RadialInterpolationDataresult{ .surface_scalar_dofs=selected, .row_offsets=std::vector<int>(positions->size()+1), .logical_radii=std::vector<double>(positions->size()), .stellar_support=*stellar_support, .vacuum_support=*vacuum_support, .stellar_radius=*stellar_radius, .infinity_radius=*infinity_radius}; std::vector<int>surface_indices(positions->size(),-1); for(std::size_tnode=0;node<selected.size();++node) { surface_indices[selected[node]]=static_cast<int>(node); } constdoublecenter_tolerance=64.0*std::numeric_limits<double>::epsilon()*result.infinity_radius; constdoubleradial_tolerance=128.0*std::numeric_limits<double>::epsilon()*result.infinity_radius; ::mfem::Vectorposition(3),target(3); for(std::size_tnode=0;node<positions->size();++node) { result.row_offsets[node]=static_cast<int>(result.surface_columns.size()); for(intcomponent=0;component<3;++component) { position[component]=(*positions)[node][component]; } constdoubleradius=detail::logical_radius(position); result.logical_radii[node]=radius; if((result.stellar_support[node]&&radius>result.stellar_radius+radial_tolerance)||(result.vacuum_support[node]&&(radius<result.stellar_radius-radial_tolerance||radius>result.infinity_radius+radial_tolerance))) { returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::invalid_geometry,.message="Asupportedscalarnodeliesoutsideitslogicalradialinterval.",.point=static_cast<int>(node)}); } if(radius<=center_tolerance) { continue; } target=position; target*=result.stellar_radius/radius; constautolocation=detail::locate_boundary_point(logical_mesh,surface_attribute,target); if(!location) { returnstd::unexpected(location.error()); } constDeformationStatusappended=append_row(scalar_space,*location,surface_indices,result); if(!appended) { returnstd::unexpected(appended.error()); } if(result.vacuum_support[node]) { target=position; target*=result.infinity_radius/radius; constautoinfinity_location=detail::locate_boundary_point(logical_mesh,infinity_attribute,target); if(!infinity_location) { returnstd::unexpected(infinity_location.error()); } } } result.row_offsets.back()=static_cast<int>(result.surface_columns.size()); returnmake_radial_interpolation(std::move(result)); } std::expected<RadialInterpolation,DeformationFailure>build_radial_interpolation(const::mfem::FiniteElementSpace&scalar_space,const::mfem::FiniteElementSpace&vector_space,::mfem::Mesh&logical_mesh,std::span<constint>stellar_element_support,std::span<constint>vacuum_element_support,intsurface_attribute,intinfinity_attribute) { returnselect_surface_scalar_dofs(scalar_space,surface_attribute).and_then([&](conststd::vector<int>&selected) { returndetail::build_radial_interpolation(scalar_space,vector_space,logical_mesh,stellar_element_support,vacuum_element_support,surface_attribute,infinity_attribute,selected); }); } }//namespaceserif::geometry::deformation::backend::mfem