constrained_radial_extension.hpp memory span utility mfem/fem/fespace.hpp mfem/linalg/sparsemat.hpp serif/geometry/deformation/radial_extension.hpp serif/geometry/deformation/detail/action_storage.hpp src/include/serif/backend/mfem/geometry/deformation/computations.hpp serif::geometry::deformation::backend::mfem::ConstrainedRadialExtension serif::geometry::deformation::backend::mfem::ConstrainedRadialExtension::Workspace serif serif::geometry serif::geometry::deformation serif::geometry::deformation::backend serif::geometry::deformation::backend::mfem #pragmaonce #include<memory> #include<span> #include<utility> #include"mfem/fem/fespace.hpp" #ifdefMFEM_USE_MPI #include"mfem/fem/pfespace.hpp" #endif #include"mfem/linalg/sparsemat.hpp" #include"serif/geometry/deformation/radial_extension.hpp" #include"serif/geometry/deformation/detail/action_storage.hpp" namespaceserif::geometry::deformation::backend::mfem{ classConstrainedRadialExtension{ public: usingInputSpace=RadialExtension::InputSpace; usingOutputSpace=RadialExtension::OutputSpace; staticconstexprboolis_linear=true; structWorkspace{ ::mfem::Vectorlocal; ::mfem::Vectorindependent; }; [[nodiscard]]conststd::shared_ptr<constRadialInterpolation>&interpolation()constnoexcept{ returnm_radial.interpolation(); } [[nodiscard]]std::span<constdouble>radial_weights()constnoexcept{ returnm_radial.radial_weights(); } [[nodiscard]]std::size_tinput_size()constnoexcept{ returnm_radial.input_size(); } [[nodiscard]]std::size_toutput_size()constnoexcept{ returnm_radial.output_size(); } [[nodiscard]]Workspacemake_workspace()const; [[nodiscard]]DeformationStatusapply(std::span<constdouble>surface,std::span<double>volume)const; [[nodiscard]]DeformationStatusapply_transpose(std::span<constdouble>volume_force,std::span<double>surface_force)const; [[nodiscard]]DeformationStatusapply(std::span<constdouble>surface,std::span<double>volume,Workspace&workspace)const; [[nodiscard]]DeformationStatusapply_transpose(std::span<constdouble>volume_force,std::span<double>surface_force,Workspace&workspace)const; private: ConstrainedRadialExtension(RadialExtensionradial,std::unique_ptr<const::mfem::SparseMatrix>prolongation,std::unique_ptr<const::mfem::SparseMatrix>restriction); [[nodiscard]]DeformationStatusvalidate_workspace(constWorkspace&workspace,std::span<constdouble>input,std::span<double>output)const; friendstd::expected<ConstrainedRadialExtension,DeformationFailure>make_constrained_radial_extension( std::shared_ptr<constRadialInterpolation>,const::mfem::FiniteElementSpace&,PowerLawRadialExtension,FixedInfinityRadialExtension); RadialExtensionm_radial; std::unique_ptr<const::mfem::SparseMatrix>m_prolongation; std::unique_ptr<const::mfem::SparseMatrix>m_restriction; }; [[nodiscard]]std::expected<ConstrainedRadialExtension,DeformationFailure>make_constrained_radial_extension( std::shared_ptr<constRadialInterpolation>interpolation, const::mfem::FiniteElementSpace&displacement_space, PowerLawRadialExtensioninterior={}, FixedInfinityRadialExtensionvacuum={}); }//namespaceserif::geometry::deformation::backend::mfem namespaceserif::geometry::deformation::backend::mfem{ inlineConstrainedRadialExtension::ConstrainedRadialExtension(RadialExtensionradial, std::unique_ptr<const::mfem::SparseMatrix>prolongation,std::unique_ptr<const::mfem::SparseMatrix>restriction): m_radial(std::move(radial)),m_prolongation(std::move(prolongation)),m_restriction(std::move(restriction)){} inlineConstrainedRadialExtension::WorkspaceConstrainedRadialExtension::make_workspace()const{ if(!m_prolongation){ return{}; } return{.local=::mfem::Vector(static_cast<int>(output_size())),.independent=::mfem::Vector(m_restriction->Height())}; } inlineDeformationStatusConstrainedRadialExtension::apply(std::span<constdouble>surface,std::span<double>volume)const{ Workspaceworkspace=make_workspace(); returnapply(surface,volume,workspace); } inlineDeformationStatusConstrainedRadialExtension::apply_transpose(std::span<constdouble>volume_force,std::span<double>surface_force)const{ Workspaceworkspace=make_workspace(); returnapply_transpose(volume_force,surface_force,workspace); } inlineDeformationStatusConstrainedRadialExtension::validate_workspace(constWorkspace&workspace, std::span<constdouble>input,std::span<double>output)const{ if(workspace.local.Size()!=static_cast<int>(output_size())||workspace.independent.Size()!=m_restriction->Height()){ returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::invalid_size,.message="Constrainedradialactionrequiresscratchallocatedforthisextension."}); } conststd::span<double>local(workspace.local.GetData(),workspace.local.Size()); conststd::span<double>independent(workspace.independent.GetData(),workspace.independent.Size()); if(constDeformationStatusstatus=deformation::detail::validate_disjoint_output(local,{input,output,independent});!status){ returnstatus; } returndeformation::detail::validate_disjoint_output(independent,{input,output}); } inlineDeformationStatusConstrainedRadialExtension::apply(std::span<constdouble>surface, std::span<double>volume,Workspace&workspace)const{ if(!m_prolongation){ returnm_radial.apply(surface,volume); } if(constDeformationStatusstatus=deformation::detail::validate_action_arguments(volume,output_size(),{{surface,input_size()}});!status){ returnstatus; } if(constDeformationStatusstatus=validate_workspace(workspace,surface,volume);!status){ returnstatus; } if(constDeformationStatusstatus=m_radial.apply(surface,{workspace.local.GetData(),static_cast<std::size_t>(workspace.local.Size())});!status){ returnstatus; } m_restriction->Mult(workspace.local,workspace.independent); ::mfem::Vectoroutput(volume.data(),static_cast<int>(volume.size())); m_prolongation->Mult(workspace.independent,output); returnvalidate_deformation_values(volume,output_size()); } inlineDeformationStatusConstrainedRadialExtension::apply_transpose(std::span<constdouble>volume_force, std::span<double>surface_force,Workspace&workspace)const{ if(!m_prolongation){ returnm_radial.apply_transpose(volume_force,surface_force); } if(constDeformationStatusstatus=deformation::detail::validate_action_arguments(surface_force,input_size(),{{volume_force,output_size()}});!status){ returnstatus; } if(constDeformationStatusstatus=validate_workspace(workspace,volume_force,surface_force);!status){ returnstatus; } //MFEM'svectorviewrequiresmutablestorage;matrixapplicationonlyreadsthisforce. const::mfem::Vectorinput(const_cast<double*>(volume_force.data()),static_cast<int>(volume_force.size())); m_prolongation->MultTranspose(input,workspace.independent); m_restriction->MultTranspose(workspace.independent,workspace.local); returnm_radial.apply_transpose({workspace.local.GetData(),static_cast<std::size_t>(workspace.local.Size())},surface_force); } inlinestd::expected<ConstrainedRadialExtension,DeformationFailure>make_constrained_radial_extension( std::shared_ptr<constRadialInterpolation>interpolation,const::mfem::FiniteElementSpace&displacement_space, PowerLawRadialExtensioninterior,FixedInfinityRadialExtensionvacuum){ #ifdefMFEM_USE_MPI if(dynamic_cast<const::mfem::ParFiniteElementSpace*>(&displacement_space)!=nullptr){ returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::incompatible_discretization,.message="Constrainedradialextensionrequiresaserialdisplacementspace."}); } #endif autoradial=make_radial_extension(std::move(interpolation),interior,vacuum); if(!radial){ returnstd::unexpected(radial.error()); } if(displacement_space.GetVDim()!=3||displacement_space.GetOrdering()!=::mfem::Ordering::byNODES|| displacement_space.GetVSize()!=static_cast<int>(radial->output_size())){ returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::incompatible_discretization,.message="Constrainedradialextensionrequiresitsthree-componentreferencespaceincomponent-blockordering."}); } const::mfem::SparseMatrix*prolongation=displacement_space.GetConformingProlongation(); if(prolongation==nullptr){ returnConstrainedRadialExtension(std::move(*radial),nullptr,nullptr); } const::mfem::SparseMatrix*restriction=displacement_space.GetConformingRestriction(); if(displacement_space.IsVariableOrder()||restriction==nullptr||prolongation->Height()!=displacement_space.GetVSize()|| prolongation->Width()!=displacement_space.GetTrueVSize()||restriction->Height()!=prolongation->Width()|| restriction->Width()!=prolongation->Height()){ returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::incompatible_discretization,.message="Constrainedradialextensionrequiresfixed-orderserialprolongationandmatchingcoefficientrestrictionmatrices."}); } returnConstrainedRadialExtension(std::move(*radial),std::make_unique<const::mfem::SparseMatrix>(*prolongation), std::make_unique<const::mfem::SparseMatrix>(*restriction)); } }//namespaceserif::geometry::deformation::backend::mfem