scaling.cpp serif/utils/laneemden/scaling.hpp algorithm cmath numbers serif serif::utils serif::utils::laneemden #include"serif/utils/laneemden/scaling.hpp" #include<algorithm> #include<cmath> #include<numbers> namespaceserif::utils::laneemden{ namespace{ constexprlongdoublefour_pi=4.0L*std::numbers::pi_v<longdouble>; boolpositive_finite(doublevalue){ returnstd::isfinite(value)&&value>0.0; } boolfinite_surface(constSolution&solution){ returnstd::isfinite(solution.n)&&solution.n>=0.0&&solution.surface&&positive_finite(solution.surface->xi)&& std::isfinite(solution.surface->derivative)&&solution.surface->derivative<0.0; } std::expected<PhysicalScales,std::string>make_scales( constSolution&solution,longdoubleradial_scale,doublecentral_density,doublegravitational_constant){ constlongdoublesurface_xi=solution.surface->xi; constlongdoubledimensionless_mass=-surface_xi*surface_xi*solution.surface->derivative; constdoubleradius=static_cast<double>(radial_scale*surface_xi); constdoublemass=static_cast<double>(four_pi*central_density*radial_scale*radial_scale*radial_scale*dimensionless_mass); constdoubleenthalpy=static_cast<double>(four_pi*gravitational_constant*central_density*radial_scale*radial_scale); constdoublealpha=static_cast<double>(radial_scale); if(!positive_finite(alpha)||!positive_finite(radius)||!positive_finite(mass)||!positive_finite(enthalpy)){ returnstd::unexpected("Lane–Emdenphysicalscalesoverfloworunderflow;chooserepresentableconsistentunits."); } std::optional<double>polytropic_constant; if(solution.n>0.0){ constdoubleconstant=static_cast<double>(enthalpy/((solution.n+1.0L)*std::pow(static_cast<longdouble>(central_density),1.0L/solution.n))); if(positive_finite(constant)){ polytropic_constant=constant; } } returnPhysicalScales{ .n=solution.n, .radial_scale=alpha, .mass=dimensions::QuantityValue<dimensions::Mass>{mass}, .radius=dimensions::QuantityValue<dimensions::Length>{radius}, .central_density=dimensions::QuantityValue<dimensions::MassDensity>{central_density}, .central_enthalpy=dimensions::QuantityValue<dimensions::SpecificEnthalpy>{enthalpy}, .gravitational_constant=dimensions::QuantityValue<dimensions::GravitationalConstant>{gravitational_constant}, .polytropic_constant=polytropic_constant }; } } std::expected<PhysicalScales,std::string>scale_from_mass( constSolution&solution, dimensions::QuantityValue<dimensions::Mass>mass, dimensions::QuantityValue<dimensions::MassDensity>central_density, dimensions::QuantityValue<dimensions::GravitationalConstant>gravitational_constant){ if(!finite_surface(solution)||!positive_finite(mass.value())||!positive_finite(central_density.value())||!positive_finite(gravitational_constant.value())){ returnstd::unexpected("Lane–Emdenmassscalingneedsafinitesurfaceandpositivefinitemass,centraldensity,andG."); } constlongdoublexi=solution.surface->xi; constlongdoubledimensionless_mass=-xi*xi*solution.surface->derivative; constlongdoublealpha=std::cbrt(mass.value()/(four_pi*central_density.value()*dimensionless_mass)); returnmake_scales(solution,alpha,central_density.value(),gravitational_constant.value()); } std::expected<PhysicalScales,std::string>scale_from_enthalpy( constSolution&solution, dimensions::QuantityValue<dimensions::SpecificEnthalpy>central_enthalpy, dimensions::QuantityValue<dimensions::MassDensity>central_density, dimensions::QuantityValue<dimensions::GravitationalConstant>gravitational_constant){ if(!finite_surface(solution)||!positive_finite(central_enthalpy.value())||!positive_finite(central_density.value())||!positive_finite(gravitational_constant.value())){ returnstd::unexpected("Lane–Emdenenthalpyscalingneedsafinitesurfaceandpositivefiniteenthalpy,centraldensity,andG."); } constlongdoublealpha=std::sqrt(central_enthalpy.value()/(four_pi*gravitational_constant.value()*central_density.value())); returnmake_scales(solution,alpha,central_density.value(),gravitational_constant.value()); } std::expected<PhysicalScales,std::string>scale_from_polytropic_constant( constSolution&solution,doublepolytropic_constant, dimensions::QuantityValue<dimensions::MassDensity>central_density, dimensions::QuantityValue<dimensions::GravitationalConstant>gravitational_constant){ if(!finite_surface(solution)||solution.n<=0.0||!positive_finite(polytropic_constant)||!positive_finite(central_density.value())|| !positive_finite(gravitational_constant.value())){ returnstd::unexpected("Lane–EmdenEOSscalingneedsn>0,afinitesurface,andpositivefiniteK,centraldensity,andG."); } constdoubleenthalpy=static_cast<double>((solution.n+1.0L)*polytropic_constant*std::pow(static_cast<longdouble>(central_density.value()),1.0L/solution.n)); autoscales=scale_from_enthalpy(solution,dimensions::QuantityValue<dimensions::SpecificEnthalpy>{enthalpy},central_density,gravitational_constant); if(scales){ scales->polytropic_constant=polytropic_constant; } returnscales; } std::expected<PhysicalSample,std::string>evaluate_physical( constSolution&solution,constPhysicalScales&scales,dimensions::QuantityValue<dimensions::Length>radius){ if(!finite_surface(solution)||solution.n!=scales.n||!std::isfinite(radius.value())||radius.value()<0.0|| !positive_finite(scales.radial_scale)||!positive_finite(scales.radius.value())||!positive_finite(scales.mass.value())|| !positive_finite(scales.central_density.value())||!positive_finite(scales.central_enthalpy.value())||!positive_finite(scales.gravitational_constant.value())){ returnstd::unexpected("Lane–Emdenphysicalevaluationrequiresmatchingfinite-surfacescalesandafinitenonnegativeradius."); } constlongdoublegravitational_mass=static_cast<longdouble>(scales.gravitational_constant.value())*scales.mass.value(); doubledensity=0.0; doublepressure=0.0; doubleenthalpy=0.0; doublemass=scales.mass.value(); doublepotential; doublegravity; if(radius.value()>=scales.radius.value()){ potential=static_cast<double>(-gravitational_mass/radius.value()); gravity=static_cast<double>(gravitational_mass/radius.value()/radius.value()); }else{ constdoublexi=std::min(radius.value()/scales.radial_scale,solution.surface->xi); constautosample=evaluate(solution,xi); if(!sample){ returnstd::unexpected(sample.error()); } constdoubletheta=std::max(0.0,sample->theta); density=scales.central_density.value()*std::pow(theta,solution.n); enthalpy=scales.central_enthalpy.value()*theta; pressure=static_cast<double>(static_cast<longdouble>(density)*enthalpy/(solution.n+1.0L)); potential=static_cast<double>(-gravitational_mass/scales.radius.value()-enthalpy); gravity=-scales.central_enthalpy.value()/scales.radial_scale*sample->derivative; mass=static_cast<double>(-four_pi*scales.central_density.value()*std::pow(static_cast<longdouble>(scales.radial_scale),3)*xi*xi*sample->derivative); } if(!std::isfinite(density)||!std::isfinite(pressure)||!std::isfinite(enthalpy)||!std::isfinite(potential)||!std::isfinite(gravity)||!std::isfinite(mass)){ returnstd::unexpected("Lane–Emdenphysicalfieldsoverflowinthesuppliedunits."); } returnPhysicalSample{ .density=dimensions::QuantityValue<dimensions::MassDensity>{density}, .pressure=dimensions::QuantityValue<dimensions::Pressure>{pressure}, .enthalpy=dimensions::QuantityValue<dimensions::SpecificEnthalpy>{enthalpy}, .potential=dimensions::QuantityValue<dimensions::GravitationalPotential>{potential}, .enclosed_mass=dimensions::QuantityValue<dimensions::Mass>{mass}, .gravity=dimensions::QuantityValue<dimensions::Acceleration>{gravity} }; } }