radial.cpp serif/backend/mfem/projection/radial.hpp cmath optional stdexcept utility mfem/fem/coefficient.hpp serif/backend/mfem/mapping/evaluator.hpp serif/backend/mfem/mapping/transformations.hpp serif/backend/mfem/shared/finite.hpp serif/utils/error/errors.hpp serif serif::utils serif::utils::projection serif::utils::projection::backend serif::utils::projection::backend::mfem serif::utils::projection::backend::mfem::detail #include"serif/backend/mfem/projection/radial.hpp" #include<cmath> #include<optional> #include<stdexcept> #include<utility> #include"mfem/fem/coefficient.hpp" #include"serif/backend/mfem/mapping/evaluator.hpp" #include"serif/backend/mfem/mapping/transformations.hpp" #include"serif/backend/mfem/shared/finite.hpp" #include"serif/utils/error/errors.hpp" namespaceserif::utils::projection::backend::mfem{ namespace{ structRadialSampler{ conststd::function<RadialValue(double)>&profile; constRadialProjectionOptions&options; ::mfem::Vectorposition; mapping::MappingPointContextcontext; std::optional<std::string>failure; doubleradius{0.0}; boolat_infinity{false}; [[nodiscard]]boollocate( ::mfem::ElementTransformation&transformation, const::mfem::IntegrationPoint&point ){ if(failure){ returnfalse; } at_infinity=false; if(options.mapping!=nullptr){ constmapping::MappingStatusstatus=options.mapping->evaluate_point(transformation,point,context); if(status==mapping::MappingStatus::AT_COMPACTIFIED_INFINITY){ transformation.Transform(point,position); if(!checks::is_finite(position)){ failure="Radialprojectionencounterednon-finitemeshcoordinatesatcompactifiedinfinity."; returnfalse; } at_infinity=true; returntrue; } if(status!=mapping::MappingStatus::VALID){ failure="Radialprojectionrequiresavalidphysicalmappingateverysampledpoint."; returnfalse; } position=context.physical_position; }else{ transformation.Transform(point,position); } radius=0.0; for(intcomponent=0;component<position.Size();++component){ position[component]-=options.center[component]; radius=std::hypot(radius,position[component]); } if(!std::isfinite(radius)){ failure="Radialprojectionencounteredanon-finitephysicalradius."; returnfalse; } returntrue; } [[nodiscard]]doublechecked_value(constRadialValue&result){ if(!result){ failure="Radialprojectionprofilerejectedasample:"+result.error(); return0.0; } if(!std::isfinite(*result)){ failure="Radialprojectionprofilereturnedanon-finitevalue."; return0.0; } return*result; } }; classRadialScalarCoefficientfinal:public::mfem::Coefficient{ public: RadialScalarCoefficient(RadialSampler&sampler,constboolintegral_mapping): m_sampler(sampler),m_integral_mapping(integral_mapping){} doubleEval(::mfem::ElementTransformation&transformation,const::mfem::IntegrationPoint&point)override{ if(!m_sampler.locate(transformation,point)){ return0.0; } if(m_sampler.at_infinity){ if(!m_sampler.options.scalar_at_infinity){ m_sampler.failure="Radialscalarprojectionatcompactifiedinfinityrequiresanexplicitscalar_at_infinitylimitinmeshcoordinates."; return0.0; } returnm_sampler.checked_value(m_sampler.options.scalar_at_infinity(m_sampler.position)); } doublevalue=m_sampler.checked_value(m_sampler.profile(m_sampler.radius)); if(m_integral_mapping&&m_sampler.options.mapping!=nullptr){ value*=m_sampler.context.mapping_determinate; } if(!std::isfinite(value)){ m_sampler.failure="Radialscalarprojectionoverflowedwhilepullingbackthephysicalvalue."; return0.0; } returnvalue; } private: RadialSampler&m_sampler; boolm_integral_mapping; }; classRadialVectorCoefficientfinal:public::mfem::VectorCoefficient{ public: RadialVectorCoefficient(RadialSampler&sampler,constintdimension): ::mfem::VectorCoefficient(dimension),m_sampler(sampler){} voidEval( ::mfem::Vector&value, ::mfem::ElementTransformation&transformation, const::mfem::IntegrationPoint&point )override{ value.SetSize(GetVDim()); value=0.0; if(!m_sampler.locate(transformation,point)){ return; } if(m_sampler.at_infinity){ if(!m_sampler.options.vector_at_infinity){ m_sampler.failure="Radialvectorprojectionatcompactifiedinfinityrequiresanexplicitvector_at_infinitylimitinmeshcoordinates."; return; } constautolimit=m_sampler.options.vector_at_infinity(m_sampler.position); if(!limit){ m_sampler.failure="Radialvectorprojectionrejecteditsinfinitylimit:"+limit.error(); return; } if(limit->Size()!=GetVDim()||!checks::is_finite(*limit)){ m_sampler.failure="Radialvectorprojectionrequiresafiniteinfinitylimitmatchingthespatialdimension."; return; } value=*limit; return; } constdoubleamplitude=m_sampler.checked_value(m_sampler.profile(m_sampler.radius)); if(m_sampler.failure){ return; } if(m_sampler.radius==0.0){ if(amplitude!=0.0){ m_sampler.failure="Aradialvectorrequireszeroamplitudeatitscentertohaveauniquevalue."; } return; } for(intcomponent=0;component<value.Size();++component){ value[component]=amplitude*(m_sampler.position[component]/m_sampler.radius); } if(!checks::is_finite(value)){ m_sampler.failure="Radialvectorprojectionoverflowedwhileevaluatingthephysicalvector."; value=0.0; } } private: RadialSampler&m_sampler; }; [[nodiscard]]intvalidate_space( ::mfem::FiniteElementSpace&space,constboolvector,constRadialProjectionOptions&options ){ const::mfem::Mesh*mesh=space.GetMesh(); if(mesh==nullptr||mesh->GetNE()==0||mesh->Dimension()!=mesh->SpaceDimension()|| mesh->SpaceDimension()<1||mesh->SpaceDimension()>3){ errors::serif_error<std::invalid_argument, errors::SERiFErrorCode::INVALID_ARGUMENT>( "Radialprojectionrequiresanonemptyvolumemeshwithonetothreespatialdimensions;" "supplyafinite-elementspaceonsuchamesh." ); } if(options.mapping!=nullptr&&options.mapping->mesh()!=mesh){ errors::serif_error<std::invalid_argument, errors::SERiFErrorCode::MISMATCHED_MESHING_MAPS>( "Radialprojectionanditsgeometrymappingmustusethesamemesh;" "supplyamappingconstructedfromfieldsontheprojectionspace'smesh." ); } constintmap_type=space.GetFE(0)->GetMapType(); for(intelement=0;element<space.GetNE();++element){ const::mfem::FiniteElement&finite_element=*space.GetFE(element); constboolscalar_element=finite_element.GetRangeType()==::mfem::FiniteElement::SCALAR; constboolsupported_scalar=scalar_element&& (map_type==::mfem::FiniteElement::VALUE||map_type==::mfem::FiniteElement::INTEGRAL); constboolsupported_vector= scalar_element&&map_type==::mfem::FiniteElement::VALUE&&space.GetVDim()==mesh->SpaceDimension(); if(finite_element.GetMapType()!=map_type|| (!vector&&(!supported_scalar||space.GetVDim()!=1))||(vector&&!supported_vector)){ errors::serif_error<std::invalid_argument, errors::SERiFErrorCode::DISCRETIZATION_ERROR>( "Radialprojectionreceivedanincompatiblefinite-elementspace.Scalarsrequirescalar" "H1/L2elements;vectorsrequireonecomponentperspatialdimension." ); } } returnmap_type; } [[nodiscard]]boolfinite_center(constRadialProjectionOptions&options){ for(constdoublecoordinate:options.center){ if(!std::isfinite(coordinate)){ returnfalse; } } returntrue; } template<typenameCoefficient> [[nodiscard]]ProjectionResultproject( ::mfem::FiniteElementSpace&space,Coefficient&coefficient,RadialSampler&sampler ){ ProjectionResultresult(std::in_place,&space); *result=0.0; result->ProjectCoefficient(coefficient); if(sampler.failure){ returnstd::unexpected(*sampler.failure); } //Nativeelementinterpolationdoesnotenforcehanging-nodeorhanging-faceconstraints. result->RestrictConforming(); if(!checks::is_finite(*result)){ returnstd::unexpected("Radialprojectionproducednon-finitefinite-elementcoefficients."); } returnresult; } } namespacedetail{ ProjectionResultproject_scalar( ::mfem::FiniteElementSpace&space, conststd::function<RadialValue(double)>&profile, constRadialProjectionOptions&options ){ constintmap_type=validate_space(space,false,options); if(!finite_center(options)){ returnstd::unexpected("Radialprojectionrequiresafinitecenter."); } RadialSamplersampler{.profile=profile,.options=options}; RadialScalarCoefficientcoefficient(sampler,map_type==::mfem::FiniteElement::INTEGRAL); returnproject(space,coefficient,sampler); } ProjectionResultproject_vector( ::mfem::FiniteElementSpace&space, conststd::function<RadialValue(double)>&radial_amplitude, constRadialProjectionOptions&options ){ static_cast<void>(validate_space(space,true,options)); if(!finite_center(options)){ returnstd::unexpected("Radialprojectionrequiresafinitecenter."); } RadialSamplersampler{.profile=radial_amplitude,.options=options}; RadialVectorCoefficientcoefficient(sampler,space.GetMesh()->SpaceDimension()); returnproject(space,coefficient,sampler); } } }