state.hpp algorithm cmath expected format span string vector serif/backend/mfem/discretization/dof_selection.hpp serif/backend/mfem/model/binding.hpp serif/model/state.hpp serif/backend/mfem/solver/seed/fitted.hpp serif/backend/mfem/mapping/evaluator.hpp serif/backend/mfem/projection/radial.hpp src/include/serif/solver/seed/state.hpp serif::solver::seed::backend::mfem::ProjectionOptions serif serif::solver serif::solver::seed serif::solver::seed::backend serif::solver::seed::backend::mfem #pragmaonce #include<algorithm> #include<cmath> #include<expected> #include<format> #include<span> #include<string> #include<vector> #include"serif/backend/mfem/discretization/dof_selection.hpp" #include"serif/backend/mfem/model/binding.hpp" #include"serif/model/state.hpp" #include"serif/backend/mfem/solver/seed/fitted.hpp" #include"serif/backend/mfem/mapping/evaluator.hpp" #include"serif/backend/mfem/projection/radial.hpp" namespaceserif::solver::seed::backend::mfem{ structProjectionOptionsfinal{ doublesurface_radius_relative_tolerance{5.0e-4}; }; template<model::StellarModelTypeModel,typenameDiscretization,typenameProfile> requires(std::same_as<Profile,RadialSeed>||std::same_as<Profile,FittedRadialSeed>)&&requires(constModel&model,constDiscretization&discretization){ requires(!std::same_as<typenamestd::remove_cvref_t<decltype(model::resolve_equilibrium_equations(model))>::ShapeForce,model::NoShapeForce>); discretization.deformation.model.surface; discretization.deformation.reference; } [[nodiscard]]automake_polytropic_state(constModel&model,constDiscretization&discretization,constProfile&seed,constProjectionOptionsoptions={}){ namespaceblocks=serif::discretization::blocks; namespacedependencies=utils::dependencies; namespaceprojection=utils::projection::backend::mfem; usingDescription=std::remove_cvref_t<decltype(model::resolve_equilibrium_equations(model))>; usingPlan=model::EquilibriumPlan<typename Model::EquationOfState, Description, dependencies::KeyList<blocks::displacement::vector>>; usingState=model::StellarState<typename Plan::Layout>; usingResult=std::expected<State,std::string>; constRadialSeed&source=serif::solver::seed::detail::radial_source(seed); constdimensions::LengthValueseed_radius=serif::solver::seed::detail::radial_radius(seed); if(!std::isfinite(options.surface_radius_relative_tolerance)||options.surface_radius_relative_tolerance<0.0){ returnResult(std::unexpected("Theradialseedsurface-radiustolerancemustbefiniteandnonnegative.")); } ifconstexpr(std::same_as<typenameDescription::SurfaceCondition,geometry::surface::Isobaric>){ if(model::resolve_equilibrium_equations(model).surface_condition.target_pressure().value()!=0.0){ returnResult(std::unexpected("AcompleteLane-Emdenseedrequiresazero-pressureisobaricsurface.")); } } std::vector<double>shape; ifconstexpr(Plan::has_shape){ constautoprepared=detail::radial_seed_surface(discretization); if(!prepared){ returnResult(std::unexpected(prepared.error())); } constauto&surface=**prepared; for(constauto&point:surface.reference_positions()){ constdoubleradius=std::hypot(point[0]-source.center[0],point[1]-source.center[1],point[2]-source.center[2]); constdoublescale=std::max(radius,seed_radius.value()); if(std::abs(radius-seed_radius.value())>options.surface_radius_relative_tolerance*scale){ returnResult(std::unexpected(std::format( "Thereferencesurfaceradius{}differsfromtheradialseedradius{}(relativeerror{},allowed{}).Chooseasphericalreferencemeshattheseedradius,resolvingitsgeometrysufficiently.", radius,seed_radius.value(),std::abs(radius-seed_radius.value())/scale,options.surface_radius_relative_tolerance))); } } shape.resize(surface.input_size(),0.0); } Statestate(model::backend::mfem::equilibrium_layout<Plan>(discretization,shape)); ::mfem::GridFunctiondisplacement(&discretization.templatebinding<blocks::displacement::vector>().space()); displacement=0.0; mapping::GridFunctionMappingEvaluatormapping(discretization.mapper,displacement,discretization.compactification); constdoublegravitational_mass=source.scales.gravitational_constant.value()*serif::solver::seed::detail::radial_mass(seed).value(); constprojection::RadialProjectionOptionsprojection_options{ .center=source.center, .mapping=&mapping, .scalar_at_infinity=[](const::mfem::Vector&)->utils::projection::RadialValue{return0.0;} }; constautophysical=[&](doubleradius){ returnevaluate_radial_seed(seed,dimensions::LengthValue{radius}); }; constautoproject=[&]<typenameTerm>(automember)->std::expected<void,std::string>{ constauto&binding=discretization.templatebinding<Term>(); constautoprofile=[&](doubleradius)->utils::projection::RadialValue{ returnphysical(radius).transform([member](constutils::laneemden::PhysicalSample&sample){return(sample.*member).value();}); }; autofield=projection::project_scalar(binding.space(),profile,projection_options); if(!field){ returnstd::unexpected(field.error()); } const::mfem::Vectorreduced=binding.gather(*field); std::copy_n(reduced.GetData(),reduced.Size(),state.templateget<Term>().begin()); return{}; }; std::expected<void,std::string>status; constautoassign=[&]<typenameTerm>(automember){ ifconstexpr(dependencies::ContainsKey<Term,typenamePlan::Values>){ if(status){ status=project.templateoperator()<Term>(member); } } }; assign.templateoperator()<blocks::density::mass>(&utils::laneemden::PhysicalSample::density); assign.templateoperator()<blocks::gravity::potential>(&utils::laneemden::PhysicalSample::potential); if(!status){ returnResult(std::unexpected(status.error())); } ifconstexpr(std::same_as<typenameModel::EquationOfState,eos::models::Polytrope>&& std::same_as<typenameDescription::SurfaceCondition,geometry::surface::Isobaric>){ usingFormulation=typenamePlan::Surface::Formulation; ifconstexpr(!std::same_as<Formulation,void>){ usingCarrier=typenameFormulation::Test; if(model::resolve_equilibrium_equations(model).surface_condition.target_pressure().value()==0.0){ //Projectionroundoffattheanalyticsurfacemustnotspoiltheexactprescribedtrace. constautoselected=serif::discretization::backend::mfem::select_boundary_dofs<typename Formulation::Support>( discretization.templatebinding<Carrier>()); for(constintdof:selected.reduced_dofs()){ state.templateget<Carrier>()[dof]=0.0; } } } } ifconstexpr(dependencies::ContainsKey<blocks::barotropic_constant::mass_normalization,typenamePlan::Values>){ //Usethenonrotatingsphericalvalue-GM/RastheinitialguessforeithersurfacelevelorBernoulliconstant. state.templateget<blocks::barotropic_constant::mass_normalization>()[0]=-gravitational_mass/seed_radius.value(); } ifconstexpr(dependencies::ContainsKey<blocks::rotation::angular_velocity,typenamePlan::Values>){ state.templateget<blocks::rotation::angular_velocity>()[0]=seed.angular_velocity; } returnResult(std::move(state)); } }