feat(solver/seed): Initial guess system
the seed system has two jobs, get the lane-emden solution for a reasonable n and project that onto the mesh. Note that the seed system does not solve lane-emden, that is in utils/laneemden (so that it can be reused by other systems). Currently SERiF only solves polytrope so the lane-emden initial guess is generally very good; however, as we move to non polytropic EOS we will still use a projected lane-emden as our initial guess for the starting model
This commit is contained in:
50
src/include/serif/solver/seed/fitted.hpp
Normal file
50
src/include/serif/solver/seed/fitted.hpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include "serif/solver/seed/polytrope.hpp"
|
||||
|
||||
namespace serif::solver::seed {
|
||||
/**
|
||||
* @brief An explicitly stretched initial guess, distinct from an equilibrium Lane–Emden scaling.
|
||||
* @note The source EOS, density and enthalpy stay unchanged at corresponding fractional radii.
|
||||
* Gravity follows the stretched density; this generally breaks hydrostatic balance, intentionally.
|
||||
*/
|
||||
struct FittedRadialSeed final {
|
||||
RadialSeed source;
|
||||
dimensions::LengthValue radius;
|
||||
dimensions::MassValue mass;
|
||||
double moment_of_inertia{};
|
||||
double angular_velocity{};
|
||||
};
|
||||
|
||||
/** @brief Evaluate the original equilibrium profile without changing its physical scaling. */
|
||||
[[nodiscard]] inline auto evaluate_radial_seed(const RadialSeed& seed, dimensions::LengthValue radius) {
|
||||
return utils::laneemden::evaluate_physical(seed.solution, seed.scales, radius);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy a radial seed onto another radius without changing its material thermodynamics.
|
||||
* @note For stretch s, enclosed mass scales as s^3, potential as s^2, gravity as s and inertia as s^5.
|
||||
* Omega becomes Omega_source/s^5, preserving the angular momentum inferred from the source guess.
|
||||
* The original PhysicalScales remain intact; no equilibrium or target-constraint satisfaction is implied.
|
||||
*/
|
||||
[[nodiscard]] std::expected<FittedRadialSeed, std::string> fit_radial_seed(const RadialSeed& seed, dimensions::LengthValue radius);
|
||||
|
||||
/** @brief Evaluate the fitted density and its consistent spherical gravity, retaining the source enthalpy. */
|
||||
[[nodiscard]] std::expected<utils::laneemden::PhysicalSample, std::string> evaluate_radial_seed(
|
||||
const FittedRadialSeed& seed, dimensions::LengthValue radius);
|
||||
|
||||
namespace detail {
|
||||
/** @brief Inspect the unchanged physical source of a strict seed. */
|
||||
[[nodiscard]] inline const RadialSeed& radial_source(const RadialSeed& seed) { return seed; }
|
||||
/** @brief Inspect the unchanged physical source of an explicitly fitted guess. */
|
||||
[[nodiscard]] inline const RadialSeed& radial_source(const FittedRadialSeed& seed) { return seed.source; }
|
||||
/** @brief Use the equilibrium radius for ordinary projection. */
|
||||
[[nodiscard]] inline dimensions::LengthValue radial_radius(const RadialSeed& seed) { return seed.scales.radius; }
|
||||
/** @brief Use the explicit destination radius for fitted projection. */
|
||||
[[nodiscard]] inline dimensions::LengthValue radial_radius(const FittedRadialSeed& seed) { return seed.radius; }
|
||||
/** @brief Retain the original spherical mass for ordinary projection. */
|
||||
[[nodiscard]] inline dimensions::MassValue radial_mass(const RadialSeed& seed) { return seed.scales.mass; }
|
||||
/** @brief Use the mass of the stretched density for fitted gravity projection. */
|
||||
[[nodiscard]] inline dimensions::MassValue radial_mass(const FittedRadialSeed& seed) { return seed.mass; }
|
||||
}
|
||||
}
|
||||
54
src/include/serif/solver/seed/polytrope.hpp
Normal file
54
src/include/serif/solver/seed/polytrope.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <concepts>
|
||||
#include <expected>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "serif/eos/models/polytropic.hpp"
|
||||
#include "serif/model/stellar_model.hpp"
|
||||
#include "serif/model/specifications/fixed_total_mass.hpp"
|
||||
#include "serif/model/specifications/fixed_angular_momentum.hpp"
|
||||
#include "serif/utils/laneemden/laneemden.hpp"
|
||||
|
||||
namespace serif::solver::seed {
|
||||
/** @brief Numerical choices for an EOS-consistent spherical starting guess, separate from solver normalization. */
|
||||
struct PolytropeOptions final {
|
||||
dimensions::QuantityValue<dimensions::GravitationalConstant> gravitational_constant{1.0};
|
||||
std::optional<dimensions::DensityValue> central_density;
|
||||
utils::laneemden::IntegrationOptions integration;
|
||||
};
|
||||
|
||||
/** @brief Own a spherical physical profile and its first rotation estimate without changing the stellar model. */
|
||||
struct RadialSeed final {
|
||||
utils::laneemden::Solution solution;
|
||||
utils::laneemden::PhysicalScales scales;
|
||||
double moment_of_inertia{};
|
||||
double angular_velocity{};
|
||||
std::array<double, 3> center{};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Fix the EOS K and requested mass, solving for central density when n differs from three.
|
||||
* @note At n=3 mass does not determine central density: supply that homology scale explicitly.
|
||||
* An explicit density specifies the initial guess only; its mass need not satisfy the final constraint.
|
||||
*/
|
||||
[[nodiscard]] std::expected<RadialSeed, std::string> make_polytropic_seed(
|
||||
const eos::models::Polytrope& equation_of_state, dimensions::MassValue mass, PolytropeOptions options = {});
|
||||
|
||||
/** @brief Read the authoritative EOS, mass and optional rotation prescription from a model. */
|
||||
template <model::StellarModelType Model>
|
||||
requires std::same_as<typename Model::EquationOfState, eos::models::Polytrope> && Model::template contains_specification<model::FixedTotalMass>
|
||||
[[nodiscard]] std::expected<RadialSeed, std::string> make_polytropic_seed(const Model& model, PolytropeOptions options = {}) {
|
||||
auto seed = make_polytropic_seed(model.equation_of_state(), model.template specification<model::FixedTotalMass>().target(), options);
|
||||
if (seed) {
|
||||
if constexpr (Model::template contains_specification<model::FixedAngularMomentum>) {
|
||||
const auto& rotation = model.template specification<model::FixedAngularMomentum>();
|
||||
seed->angular_velocity = rotation.target().value() / seed->moment_of_inertia;
|
||||
seed->center = rotation.center();
|
||||
}
|
||||
}
|
||||
return seed;
|
||||
}
|
||||
}
|
||||
9
src/include/serif/solver/seed/state.hpp
Normal file
9
src/include/serif/solver/seed/state.hpp
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "serif/backend/mfem/solver/seed/state.hpp"
|
||||
#include "serif/backend/mfem/solver/seed/fitted.hpp"
|
||||
|
||||
namespace serif::solver::seed {
|
||||
using backend::mfem::make_polytropic_state;
|
||||
using backend::mfem::fit_radial_seed;
|
||||
}
|
||||
Reference in New Issue
Block a user